代码改变世界

C语言0长度数组(柔性数组)

2017-04-28 08:28  tlnshuju  阅读(892)  评论(0编辑  收藏  举报

0长度数组,又称为柔性数组(flexible array)。通经常使用来实现变长数组。常见于TLV(type-length-value)的数据结构中。

在标准 C 和 C++ 中,不同意用 0 长度数组,但在 GNU C 中,却能够定义 0 长度数组(在C99之前写成长度为0,C99中能够直接不写索引)。一般会拿手冊中给的样例来说明

struct line {
         int length;
         char contents[0]; // char contents[]; //C99
}


从打印出来的结构体尺寸 sizeof (struct line) = 4 (和编译器有关,看内存对齐规则),能够看到contents[0]不占有空间(不同于字符指针类型),它存在的优点是在结构体的后面同意我们自己申请一定大小的存储空间,而不是每次都使用 MAX_SIZE 大小的结构体。通常的用法例如以下:

int this_length = 60;
struct line *thisline = (struct line *)malloc (sizeof (struct line) + this_length);
thisline->length = this_length;

通过測试代码能够发现:为0长度数组分配的空间紧跟着该该结构体最后一个字段之后,并且释放结构体指针后。我们自己分配的空间也会释放(不同于有些网上的说法)。所以须要注意的是我们释放的是由某个指针指向的内存空间,而非指针,指针变量是在程序结束后回收的,所以在我们释放一段内存之后要将其置为NULL,否则还是能够訪问的,只是訪问到的都是垃圾数据。


測试代码:FlexibleArray.c
#include <stdio.h>
#include <stdlib.h>

struct line {
         int length;
         char test;
         char contents[0];//or <span style="font-family: Arial, Helvetica, sans-serif;">char contents[];</span>
    };

int main(){
    int i;
    
    int this_length = 60;
    struct line *thisline = (struct line *)malloc (sizeof (struct line) + this_length);
    thisline->length = this_length;

    printf ("thisline addr is %p\n", thisline);
    printf ("thisline->length field value is %d\n",thisline->length);
    printf ("thisline->length field addr is %p\n", &thisline->length);
    printf ("thisline->test field addr is %p\n", &thisline->test);
    printf ("thisline->contents field addr is %p\n", &thisline->contents);
    printf ("thisline->contents[0] addr is %p\n", &thisline->contents[0]);
    printf ("thisline->contents[0] addr is %p\n", &thisline->contents[1]);

    for(i=0; i<3; i++){
        thisline->contents[i] = 'a' + i;
    }
    
    char *p = thisline->contents;
    printf("%c\n", p[2]);
    
    free(thisline);
    //After free , when we access the space ,we got garbage data;
    printf("%d\n", thisline->length);
    printf("%c\n", p[2]);

    return 0;
}

执行效果:

update:2015-4-8
FlexibleArray2.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct line {
         int length;
         char test;
         char contents[0];
    };

int main(){
    int i;
    char s[20] = "Hello World.";
    int that_length = 60;
    struct line thisline = {5, 'A', {'a', 'b', 'c'}};

    struct line *thatline = malloc(sizeof(struct line) + that_length);
    thatline->length = that_length;
    for(i=0; i<3; i++){
        thatline->contents[i] = 'A' + i;
    }


    char *p = thatline->contents;
    printf("%c\n", p[2]);

    free(thatline->contents);
   thatline->contents = thisline.contents;
// error: incompatible types when assigning to type ‘char[]’ from type ‘char *’
    p = thatline->contents;
   printf("%c\n", p[2]);

    return 0;
}


能够看到根本无法改动一个实例中contents的指向,所以占用的是完整的内存区间。不能指向单独的地址空间。


FlexibleArray3.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct line {
         int length;
         char test;
         char contents[];
    };

int main(){
    struct line thisline = {5, 'A', {'a', 'b', 'c'}};

    return 0;
}


能够看到严格的弹性数组和长度0的数组还是有差别的,这里就不能使用静态初始化。仅仅能像FlexibleArray1.c中那样操作。