0长度数组

一、零长数组

    在标准 C 或者 C++ 中由于不支持 0 长度的数组,所以 int array[0]; 这样的定义是非法的。不过有些编译器(如GCC)的扩展功能支持 0 长度的数组。

    在 C 中,0 长度的数组的主要用途是用来作为结构体的最后一个成员,然后用它来访问此结构体对象之后的一段内存(通常是动态分配的内存)。由于其非标准性,在程序中尽量避免使用 0 长度的数组。作为替换,可以使用 C99 标准中的不完整数组来替换 0 长度的数组定义。如:

        typedef struct _X {
            int a;
            char array[]; //注意,因为是不完整数组,因此不可以计算sizeof(X),无法编译通过
        } X;

    在GNU的gcc-4.4.0的官方指南中的5.14节Arrays of Length Zero一节中,它是如此写道:

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

        //...ommit code here

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

    这个用法主要用于变长Buffer,struct line的大小为4,结构体中的contents[0]不占用任何空间,甚至是一个指针的空间都不占(待会儿下面的示例代码会验证),contents在这儿只是表示一个常量指针,这个特性是用编译器来实现的,即在使用thisline->contents的时候,这个指针就是表示分配内存地址中的某块buffer,比如malloc (sizeof (struct line) + this_length)返回的是0x8f00a40,thisline->contents指向的位置就是(0x8f00a40 + sizeof(struct line)),而这儿sizeof(struct line)仅仅是一个int的四字节。

    对于这个用法,我们定义的结构体指针可以指向任意长度的内存buffer,这个技巧在变长buffer中使用起来相当方便。可能有朋友说,为什么不把最后的contents直接定义为一个指针呢?这儿的差别是这样的,如果定义为一个指针,它需要占用4Bytes,并且在申请好内存后必须人为赋地址才可以。如果使用这个用法,这个常量指针不占用空间,并且无需赋值。

    但是,方便并不是绝对的,在释放分配的内存的时候,由于函数free会认为*thisline 只是指向一个4字节的指针,即只会释放length的空间,而对于后面占据大头的buffer却视而不见,这个就需要人为干预;而对于后面的声明指针的方式,则可以直接用Free(thisline->contents)的方式释放掉分配的内存。(这地方说的不明白,让我理解之后,感觉此话是错误的。)

    如果将零长数组array换成指针*array来使用的话,指针必须重新分配一段内存之后才能使用,那么当想要用socket发送结构体指针的时候,并不会将指针array申请的内存发送过去,因为是不连续的,所以接受socket发送来的数据后,会发现该数据并不是自己想要的

    ASSERT:除非必要,不要轻易使用这个功能,GNU C下可以编译通过,所以你在使用vc++,那就不用尝试了,编译都无法通过。

01  #include <stdio.h>
02  #include <stdlib.h>
03  #include <string.h>
04 
05  #define offsetof(S,t)     (size_t)&(((S *)0)->t)     //求结构体中偏移量的宏,C++中存在此宏,C中需自己定义
06 
07  struct  zero_arry_t {
08      unsigned  int  i;
09      char  arry [];
10  };
11 
12  struct  Test {
13      int  len;
14      char  content [ 0 ];
15  };
16 
17  typedef  struct  _S1 {
18      char  a;
19      char b;
20      double  c;
21  } S1;
22 
23  typedef  struct  _S2 {
24      char  a;
25      char b;
26      double  c;
27  } __attribute__(( packed))  S2;     //__attribute__((packed))的作用就是告诉编译器取消结构在编译过程中的优化对齐
28 
29  typedef  struct  _Y
30  {
31  int  a;
32  int b;
33  char  c;
34  char  content [ 0 ];
35  }  Y;
36 
37 
38  int  main()
39  {
40      //验证0长度数组    
41      char  c0  =  'a' ,  c1  =  'b' ,  c2 = 'c' ,  c3 = 'd';
42      printf( "c0=%c, c1=%c, c2=%c, c3=%c \n &c0=%p, &c1=%p, &c2=%p, &c3=%p \n " ,  c0 ,  c1 ,  c2 ,  c3 ,  & c0 ,  & c1 , & c2 ,  & c3);                           
43      struct  Test  t;
44      t . len  =  0x01020304;
45      char  * q  =  t . content;
46      printf( "sizeof(t)=%u, sizeof(t.content)=%u \n " ,  sizeof( t ), sizeof( t . content));     //打印4 0, content本身不占空间
47      printf( "&t=%p, &t.len=%p, t.content=%p, &t.content=%p \n " ,  & t ,  & t . len ,  t . content ,  & t . content); // 
48      strcpy( t . content ,  "123");
49      //发现 c0 c1 c2 c3的位置的内容被p->content所修改 
50      printf( "c0=%c, c1=%c, c2=%c, c3=%c \n &c0=%p, &c1=%p, &c2=%p, &c3=%p \n " ,  c0 ,  c1 ,  c2 ,  c3 ,  & c0 ,  & c1 , & c2 ,  & c3); 
51     
52      char  buf [ 1024 ]  =  { 0 };
53      struct  Test  *= ( struct  Test  *) buf;
54     p -> len  =  0x01020304;
55      strcpy( p -> content ,  "abcd"); 
56      printf( " \n p=&buf=%p, p->content=%p, p->content=%s \n " ,  buf , p -> content , p -> content); 
57      int  k;
58      for( k = 0k < 10++ k)     //注意观察这十个位置的值
59          printf( "address %p: buf[%d]=%d \n " ,  buf + k ,  k ,  buf [ k ]); 
60 
61      //关于offsetof宏,以及 __attribute__((packed))属性 
62      printf( " \n sizeof(S1)=%u, offsetof(S1,c)=%u \n " ,  sizeof( S1 ), offsetof( S1 , c));
63      //使用__attribute__后,结构体大小和成员的偏移量都发生变化
64      printf( "sizeof(S2)=%u, offsetof(S2,c)=%u \n " ,  sizeof( S2 ), offsetof( S2 , c));
65      //对于有padding(补齐)的结构体Y,其sizeof(Y)和offsetof(Y, content)的大小不一致,参考这个帖子
66      printf( "sizeof(Y)=%u, offsetof(Y, content)=%u, offsetof(Y, c)=%u \n " ,  sizeof( Y ),  offsetof( Y ,  content ), offsetof( Y ,  c));
67     
68      getchar();
69      return  0;
70  }  

    运行结果截图如下:

 二、0长度数组使用

GNU C中允许使用零长度的数组。它们作为结构体的最后一个元素十分有用,在ISO C89中,你需要给目录分配长度1,意味着要浪费空间或者使malloc的参数变得复杂。在ISO C99中,你可以使用灵活的数组元素,只是在语法和主义上有微小的差异。
·   灵活的数组元素写为contents[]不带0。
·   灵活的数组元素具有不完全的类型,所以sizeof操作也许不能被应用。 作为零长度数组的原始实现的奇怪之处,sizeof被赋值为0。
 
灵活的数组元素可以作为结构体的最后一个元素出现当其它元素非空时。GCC现在在任何地方允许零长度数组。定义仅含有一个零长度数组的结构体,无论如何,你可能会遇到问题。这样的用法被反对,我们建议仅当灵活数组元素被允许的地方使用零长度数组。
example:
#include
#include

struct arr{

       int a;

       char buff[0];

} 

int main(int argc,char **argv)

{

       struct arr *ptr;

 

       ptr = (struct att *)malloc(sizeof(struct arr)+4);

 

       if(ptr == NULL){

              printf(“malloc failed\n”);

              reutn -1;

}

memset(ptr,0x30,sizeof(struct arr)+4);

printf("0x%x0x%x0x%x0x%x\n",

ptr->buff[0],ptr->buff[1],ptr->buff[2],ptr->buff[3]);

 

char b[0];

printf(“%d %d %d\n”,sizoef(struct arr),sizoef(int),sizeof(b[0]));

}

 

其中malloc分配了数据结构struct arr的大小再加上4个字节大小,实际上sizoef(struct arr) = 4 = sizoef(int),不包括零长度数组的大小,所以memset之后,buff的内容都是0x30sizeof(b[0]) = 1


参考文章:

[1].http://blog.csdn.net/goldfighter/article/details/7186394

[2].http://blog.csdn.net/goldfighter/article/details/7186394


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值