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. 深入淺出變長結構體

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM