柔性数组成员

        如果需要在结构体中存放一个动态长度的字符串,一般的做法,是在结构体中定义一个指针成员,该指针指向该字符串所在的动态内存空间,例如:

typedef struct test
{
  int a;
  double b;
  char *p;
};

  使p指向字符串,但这种方法造成字符串与结构体是分离的,不利于操作。可以把字符串跟结构体直接连在一起:

char a[] = "hello world";
struct test *stpTest = (struct test *)malloc(sizeof(struct test)+strlen(a)+1 );
strcpy(stpTest + 1, a );

        这样,(char*)(stpTest + 1)就是字符串"hello world"的地址了。但是,使用(char*)((stpTest + 1)不方便。

 

        因此,C99中引入了柔性数组成员的概念。C99 中,结构体中的最后一个元素允许是未知大小的数组,称为柔性数组成员(flexible  array  member),柔性数组成员前面必须至少还有一个其他成员,而且柔性数组成员必须是结构体的最后一个成员。一个包含柔性数组成员的结构体或(递归的)包含这样结构体的联合体,不能成为一个结构体的成员或数组的元素。

 

        柔性数组成员只作为一个符号地址存在,对包含柔性数组成员的结构体执行sizeof操作, 返回的结构大小不包括柔性数组的内存。包含柔性数组成员的结构用malloc()函数进行内存的动态分配时,通常分配的内存应该大于结构的大小,以适应柔性数组的预期大小。

        使用柔性数组成员,不需要使用指针来分配内存,节约一个指针变量所占内存大小,也使内存申请方式更加便捷;分配的内存连续,管理与释放简单,只需要一次操作。比如下面的例子:

typedef struct
{
    int len;
    char buf[];
}testflex;

int main()
{
    testflex *tf = NULL;
    tf = malloc(sizeof(testflex) + 100 * sizeof(char));
    strcpy(tf->buf, "hello, world");

    printf("sizeof testflex is %d\n", sizeof(testflex));
    printf("tf->buf is %s\n", tf->buf);
    free(tf);
}

        运行结果如下:

sizeof testflex is 4
tf->buf is hello, world

        结构体testflex中的buf就是一个柔性数组成员。sizeof(testflex)结果为4,也就是int类型变量len的长度,可见柔性数组成员不占用结构体的空间。同时tf->buf就是”hello world”的首地址,不需要再使用(char *)(tf + 1)这么丑陋的代码了。因buf是不完整类型,所以不能执行sizeof操作,比如下面的代码:

testflex tf;
printf("sizeof testflex is %d, sizeof buf is %d\n", sizeof(tf), sizeof(tf.buf));

        就会报错:

error: invalid application of 'sizeof' to incomplete type 'char[]'

         其实,GCC在C99之前,就已经支持类似的语法了, GCC支持零长数组成员,比如上面的结构体,在GCC中, 还可以这样写:

typedef struct
{
    int len;
    char buf[0];
}testflex;

         上面的程序,改成这种写法,得到的结果是一样的。不过零长数组支持sizeof操作,因此下面的代码:

typedef struct
{
    int len;
    char buf[0];
}testflex;

int main()
{
    testflex tf;
    printf("sizeof testflex is %d, sizeof buf is %d\n", sizeof(tf), sizeof(tf.buf));
}

         得到的结果如下:

sizeof testflex is 4, sizeof buf is 0

  应当尽量使用标准形式。注意C89不支持这种东西,C99把它作为一种特例加入了标准。但是C99所支持的是柔性数组成员( int a[] ),而不是零长数组成员( int a[0] )。GCC支持零长数组成员。

 

         下面是C99和GCC的相关文档:

C99标准的草案:

        the last member of a structure with more than one named member may have incomplete array type; such a structure (and any union containing, possibly recursively, a member that is such a structure) shall not be a member of a structure or an element of an array.

        The last element of a structure with more than one named member may have an incomplete array type; this is called a flexible array member. In most situations, the flexible array member is ignored. In particular, the size of the structure is as if the flexible array member were omitted except that it may have more trailing padding(为保证内存对齐而加上的填充字节)than the omission would imply.

        However, when a . (or ->)operator has a left operand that is (a pointer to) a structure with a flexible array member and the right operand names that member, it behaves as if that member were replaced with the longest array (with the same element type) that would not make the structure larger than the object being accessed; the offset of the array shall remain that of the flexible array member, even if this would differ from that of the replacement array. If this array would have no elements, it behaves as if it had one element but the behavior is undefined if any attempt is made to access that element or to generate a pointer one past it.

         C99中支持结构体的最后一个元素可以是不完整数组类型(incomplete array type, 未知大小的数组,An array type of unknown size is an incomplete type)。这种结构体,以及(递归地)以这种结构体为成员的联合体,不能是其他结构体的成员,也不能是数组中的元素。

         包含多个命名成员的结构体,其最后一个成员可以是不完整数组类型,这被称为柔性数组成员(flexible array member)。该成员在计算结构体大小(sizeof)时被忽略。

         假设A为包含柔性数组成员的结构体(或指针),B为该柔性数组,则A.B(或A->B)这种行为,相当于使用一个相同类型的常规数组替换该柔性数组,该数组的长度,是保证该结构体不会被越界访问的最大长度。

         比如下面的声明:

struct s { int n; double d[]; };
struct ss { int n; double d[1]; };

         则下面的三个表达式具有相同的值:

sizeof (struct s)
offsetof(struct s, d)
offsetof(struct ss, d)

         如果sizeof(double)为8,则下面的语句:

struct s *s1;
struct s *s2;
s1 = malloc(sizeof (struct s) + 64);
s2 = malloc(sizeof (struct s) + 46);

         行为上等同于下面的语句:

struct { int n; double d[8]; } *s1;
struct { int n; double d[5]; } *s2;

         下面的语句:

s1 = malloc(sizeof (struct s) + 10);
s2 = malloc(sizeof (struct s) +  6);

         行为上等同于下面的语句:

struct { int n; double d[1]; } *s1, *s2;

         并且:

    double *dp;
    dp = &(s1->d[0]);       // Permitted
    *dp = 42;               // Permitted
    
    dp = &(s2->d[0]);       // Permitted
    *dp = 42;               // Undefined behavior

https://busybox.net/~landley/c99-draft.html#6.7.2.1

 

GCC:

         GCC允许静态初始化柔性数组成员。这等价于定义一个新的结构体,它包含原结构体,其后跟着一个具有足够长度的数组,该数组可以包容所有的初始化数据。比如下面的例子,f1和f2是等价的:

struct f1 {
int x; int y[];
} f1 = { 1, { 2, 3, 4 } };

struct f2 {
struct f1 f1; int data[3];
} f2 = { { 1 }, { 2, 3, 4 } };

         注意,这种扩展只有在额外数据出现在顶层对象的末尾时才有意义,否则有可能会覆盖掉后续的数据。因此,除非包含柔性数组的结构体是最顶层的对象,否则GCC禁止这样的非空初始化,比如:

struct foo { int x; int y[]; };
struct bar { struct foo z; };

struct foo a = { 1, { 2, 3, 4 } };        // Valid.
struct bar b = { { 1, { 2, 3, 4 } } };    // Invalid.
struct bar c = { { 1, { } } };            // Valid.
struct foo d[1] = { { 1, { 2, 3, 4 } } };  // Invalid.

https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html

 

参考:

http://blog.csdn.net/ce123/article/details/8973073

转载于:https://www.cnblogs.com/gqtcgq/p/7247083.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值