GNU C中的零長數組

C語言沒有STL,缺乏對動態長度字符串功能的支持;同時,C使用'\0'判斷字符串的結尾,不具有二進制安全性。然而在程序開發時,我們可以通過自定義的方式,實現簡易的動態長度字符串功能。
動態長度字符串結構定義如下:

struct DynamicBuffer
{
  unsigned len;  // 實際長度
  unsigned free;  // 剩余可用空間大小
  char data[0];  // 實際內容
};

其中,較為少見的是DynamicBuffer結構中的最后一項——定義了長度為0的字符數組。零長數組是在GNU C中定義的一項功能:

Zero-length arrays are allowed in GNU C. They are very useful as the last element of a structure which is really a header for a variable-length object.

在其他環境中編譯含有零長數組的代碼可能會導致編譯錯誤。

在DynamicBuffer的定義中,我們當然也可以用char*來保存字符串數據。但是,使用零長數組代替數組指針具有以下優勢:

  • 指針占用存儲空間;而零長數組則不占用結構體空間。
  • 使用指針需要初始化,使用時需要間接尋址;零長數組不需要初始化,內存地址和后面的元素地址相同,數組名可以直接當作指針使用。

程序測試用例如下:

#include <stdio.h>
#include <stdlib.h>
#define LENGTH 5

struct DynamicBuffer1
{
    unsigned len;
    unsigned free;
    char *data;
}__attribute((packed));

struct DynamicBuffer2
{
    unsigned len;
    unsigned free;
    char data[0];
}__attribute((packed));

int main()
{
    struct DynamicBuffer1 *b1;
    struct DynamicBuffer2 *b2;
    int i;

    printf("DynamicBuffer1 length: %d\n",sizeof(struct DynamicBuffer1));
    printf("DynamicBuffer2 length: %d\n",sizeof(struct DynamicBuffer2));

    b1=(struct DynamicBuffer1*)malloc(sizeof(struct DynamicBuffer1));
    b1->len=0;
    b1->free=LENGTH;
    b1->data=(char *)malloc(sizeof(char)*LENGTH + 1);
    b1->data[0] = 100;
    b1->free--;
    b1->len++;
    printf("\nb1->len=%d,b1->free=%d,*(b1->data)=%d\n",b1->len,b1->free,*b1->data);
    printf("b1\t\t address: %p\n", b1);
    printf("b1->len\t address: %p\n", &(b1->len));
    printf("b1->free\t address: %p\n", &(b1->free));
    printf("b1->data\t address: %p\n", b1->data);

    b2=(struct DynamicBuffer2*)malloc(sizeof(struct DynamicBuffer2)+sizeof(char)*LENGTH + 1);
    b2->len=0;
    b2->free=LENGTH;
    for(i=0;i<LENGTH;i++)
    {
        b2->data[i]=i;
        b2->free--;
        b2->len++;
    }
    printf("\nb2->len=%d,b2->free=%d\n",b2->len,b2->free);
    printf("b2\t\taddress: %p\n", b2);
    printf("b2->len\taddress: %p\n", &(b2->len));
    printf("b2->free\taddress: %p\n", &(b2->free));
    printf("b2->data\taddress: %p\n", b2->data);
    free(b1->data);
    free(b1);
    free(b2);
}

用例輸出:

DynamicBuffer1 length: 16
DynamicBuffer2 length: 8
b1->len=1,b1->free=4,*(b1->data)=100
b1 address: 0x1ae7010
b1->len address: 0x1ae7010
b1->free address: 0x1ae7014
b1->data address: 0x1ae7030
b2->len=5,b2->free=0
b2 address: 0x1ae7050
b2->len address: 0x1ae7050
b2->free address: 0x1ae7054
b2->data address: 0x1ae7058

可見,通過零長數組定義的DynamicBuffer2占用的內存空間只有8個字節(兩個整形變量的空間),且data數組地址緊鄰DynamicBuffer2的內存地址。Redis的SDS API也使用了類似的方式,提供了二進制安全的動態長度字符串功能。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容