• C语言:变长结构体


    一. 概述

    在Linux程序中,经常会看到形如下面的结构体定义

    struct xfrm_algo {
       char  alg_name[64];
       unsigned int alg_key_len;    /* in bits */
       char  alg_key[0];
    };

    这里,最奇怪的是结构体最后一个元素, 是一个零长度的字符数组

    这里先解释一下结构体的作用。
    xfrm_algo是一个定义密钥算法的结构体,alg_name存放算法名称,alg_key_len存放密钥长度(单位是bit),alg_key存放密钥. 因为同一个算法,有可能会使用不同长度的密钥。

    如AES, 就有128位、192位和256位三种密钥。 所以,在定义这样一个密钥算法的结构体时,就要求不定长的结构体,而零长数组就可实现这一点。


    当然,我们也可以使用指针来代替

    struct xfrm_algo {
       char  alg_name[64];
       unsigned int alg_key_len;    /* in bits */
       char *  alg_key;
    };

    下面,分别用指针和零长数组实现不定长结构体。

    方法1:定义一个xfrm_algo结构体变量,再为alg_key成员动态创建内存

    这种情况下,实际的xfrm_algo结构体和密钥是分离的

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    
    void print_hex( unsigned char *buf, int len);
    
    struct xfrm_algo
    {
            char alg_name[64];
            unsigned int alg_key_len;
            unsigned char * alg_key;
    };
    
    int main( void )
    {
            char alg[] = "AES";
            unsigned char key[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /
                                    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
            struct xfrm_algo algo1;
            memcpy( algo1.alg_name, alg, sizeof(alg) );
            algo1.alg_key_len = sizeof(key) * 8;
            if( ( algo1.alg_key = (unsigned char *)malloc( sizeof(key) ) ) == NULL )
            {
                    perror("malloc");
                    return -1;
            }
            memcpy( algo1.alg_key, key, sizeof(key) );
            printf("sizeof(struct xfrm_algo) = %d/n", sizeof(struct xfrm_algo));
            printf("algo1: 0x%08x/n", &algo1);
            printf("/talg_name : 0x%08x(%s)/n", algo1.alg_name, algo1.alg_name);
            printf("/talg_key_len: 0x%08x(%d)/n", &algo1.alg_key_len, algo1.alg_key_len);
            printf("/talg_key : 0x%08x", algo1.alg_key);
            print_hex( algo1.alg_key, sizeof(key) );
            free(algo1.alg_key);
            return 0;
    }
    
    void print_hex( unsigned char *buf, int len)
    {
            int i = 0;
            printf("(");
            for( i = 0; i < len; i++ )
            {
                    printf("0x%02x ", buf[i]);
            }
            printf(")/n");
    }

    执行结果:
    ./struct_pointer1
    sizeof(struct xfrm_algo) = 72
    algo1: 0xbff54108
            alg_name   : 0xbff54108(AES)
            alg_key_len: 0xbff54148(128)
            alg_key    : 0x09b2f008(0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f )

    从输出可观察到, alg_key_len与alg_key是分离的

    方法2: 直接为xfrm_algo和已知的密钥动态创建内存

    此时,xfrm_algo结构体和密钥是连续的。

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    
    void print_hex( unsigned char *buf, int len);
    
    struct xfrm_algo
    {
            char alg_name[64];
            unsigned int alg_key_len;
            unsigned char * alg_key;
    };
    
    int main( void )
    {
            char alg[] = "AES";
            unsigned char key[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /
                                    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
            struct xfrm_algo *palgo = NULL;
            if( ( palgo = (struct xfrm_algo * )malloc( sizeof(struct xfrm_algo) + sizeof(key) ) ) == NULL )
            {
                    perror("malloc");
                    return -1;
            }
            memcpy( palgo->alg_name, alg, sizeof(alg) );
            palgo->alg_key_len = sizeof(key) * 8;
            palgo->alg_key = (unsigned char *)( palgo + 1 );
            memcpy( palgo->alg_key, key, sizeof(key) );
            printf("sizeof(struct xfrm_algo) = %d/n", sizeof(struct xfrm_algo));
            printf("palgo: 0x%08x/n", palgo);
            printf("/talg_name : 0x%08x(%s)/n", palgo->alg_name, palgo->alg_name);
            printf("/talg_key_len: 0x%08x(%d)/n", &palgo->alg_key_len, palgo->alg_key_len);
            printf("/talg_key : 0x%08x", palgo->alg_key);
            print_hex( palgo->alg_key, sizeof(key) );
            free(palgo);
            return 0;
    }
    
    void print_hex( unsigned char *buf, int len)
    {
            int i = 0;
            printf("(");
            for( i = 0; i < len; i++ )
            {
                    printf("0x%02x ", buf[i]);
            }
            printf(")/n");
    }

    执行结果:
    $ ./struct_pointer2
    sizeof(struct xfrm_algo) = 72
    palgo: 0x096bd008
    alg_name : 0x096bd008(AES)
    alg_key_len: 0x096bd048(128)
    alg_key : 0x096bd050(0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f )

    从输出可观察到, alg_key_len与alg_key是连续的。这里,alg_key似乎是多余的,因为我们总能使用(unsigned char *)( palgo + 1 )得到key的首地址。

    方法3:零长度数组

    在标准C语言中,是不允许零长度数组的。但 GNU C 允许。

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    
    void print_hex( unsigned char *buf, int len);
    
    struct xfrm_algo
    {
            char alg_name[64];
            unsigned int alg_key_len;
            unsigned char alg_key[0];
    };
    
    int main( void )
    {
            char alg[] = "AES";
            unsigned char key[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /
                                    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
            struct xfrm_algo *palgo = NULL;
            if( ( palgo = (struct xfrm_algo *)malloc( sizeof(struct xfrm_algo) + sizeof(key) ) ) == NULL )
            {
                    perror("malloc");
                    return -1;
            }
            memcpy( palgo->alg_name, alg, sizeof(alg) );
            palgo->alg_key_len = sizeof(key) * 8;
            memcpy( palgo->alg_key, key, sizeof(key) );
            printf("sizeof(struct xfrm_algo) = %d/n", sizeof(struct xfrm_algo));
            printf("palgo: 0x%08x/n", palgo);
            printf("/talg_name : 0x%08x(%s)/n", palgo->alg_name, palgo->alg_name);
            printf("/talg_key_len: 0x%08x(%d)/n", &(palgo->alg_key_len), palgo->alg_key_len);
            printf("/talg_key : 0x%08x", palgo->alg_key);
            print_hex( palgo->alg_key, palgo->alg_key_len / 8 );
            free(palgo);
            return 0;
    }
    
    void print_hex( unsigned char *buf, int len)
    {
            int i = 0;
            printf("(");
            for( i = 0; i < len; i++ )
            {
                    printf("0x%02x ", buf[i]);
            }
            printf(")/n");
    }

    执行结果:

    ./struct_array
    sizeof(struct xfrm_algo) = 68
    palgo: 0x0980d008
            alg_name   : 0x0980d008(AES)
            alg_key_len: 0x0980d048(128)
            alg_key    : 0x0980d04c(0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f )

    xfrm_algo结构体大小为68个字节, alg_key[0]不占存储空间,和方法1、2相比,少了个unsigned char *指针,但可移植性不比前两种方法。

    二. 这种写法的优势

    结构体最后使用0或1的长度数组的原因,主要是为了方便的管理内存缓冲区,如果你直接使用指针而不使用数组,那么,你在分配内存缓冲区时,就必须分配结构体一次,然后再分配结构体内的指针一次,(而此时分配的内存已经与结构体的内存不连续了,所以要分别管理即申请和释放)。

    而如果使用数组,那么只需要一次就可以全部分配出来,反过来,释放时也是一样,使用数组,一次释放,使用指针,得先释放结构体内的指针,再释放结构体。还不能颠倒次序。

    其实变长结构体就是分配一段连续的的内存,减少内存的碎片化,简化内存的管理

    三. 应用场景

    <1>Socket通信数据包的传输;

    <2>解析数据包,如笔者遇到的问题。

    <3>其他可以节省空间,连续存储的地方等。

    参考链接:

    1. 变长结构体的表示方法

    2. 深入浅出变长结构体

  • 相关阅读:
    xgboost系列之应用xgboost的注意事项
    【pandas】pandas.DataFrame.rename()---重置索引名称
    【剑指offer】复杂链表的复制
    【剑指offer】二叉树中和为某一值的路径,C++实现
    设计模式21 访问者模式
    设计模式20 迭代器模式
    设计模式19 中介者模式
    设计模式18 观察者模式
    设计模式17 状态模式
    设计模式16 责任链模式
  • 原文地址:https://www.cnblogs.com/yongdaimi/p/14120907.html
Copyright © 2020-2023  润新知