今天浏览酷壳上一篇文章http://coolshell.cn/articles/11377.html时,发现了有关“零长度的数组”的一些知识,其实之前在论坛中也见过这种用法,但是当时对此不太理解,时间一长就慢慢淡忘了该知识点。于是今天特意查了资料,总算弄清“零长度的数组”即柔性数组这一知识点,现在记录如下。
零长度的数组一般见于结构体中,而直接声明零长度数组,例如,
char str[0];是无法编译通过的。而网上说零长度的数组一般是放在结构体的最后一个成员的位置处,但是我试了下,放在中间也是可以的。
先说说放在最后位置处的情况:
结构体声明如下:
struct str { int len; int b; char s[0]; };注意这里可能也无法编译通过,没关系,改为char s[];这样就可以了。这里的char s[0]指的是一个变长数组,也相当于指针吧,不过跟指针却有本质的区别,如果声明成指针,那么指针指向的内存空间可能跟成员b的空间不连续,而声明成char s[0]则是连续的。
这里你可能对结构体struct str的大小不太明确,到底是是多少呢?经过测试发现是8,即sizeof(int) + sizeof(int),也就是说s指向的空间不属于结构体str,这块空间紧跟着结构体str所代表的空间。
上文指出char s[0]指向的空间紧跟着结构体所代表的空间,因此对于一个struct str *型的指针变量申请内存有如下方法:
s = (struct str *)malloc(sizeof(struct str) + 10);表示给s申请了10个字节的空间。
下面看一个例子:
#include <stdio.h> #include <string.h> #include <stdlib.h> struct str { int len; int b; char s[0]; }; int main(int argc, char *argv[]) { struct str *s = NULL; printf("sizeof(struct str) = %d\n", sizeof(struct str)); s = (str *)malloc(sizeof(struct str) + 10); s->len = 100; s->b = 50; strcpy(&s->s[0], "abc"); puts(s->s); printf("sizeof(struct str) = %d\n", sizeof(struct str)); printf("addr of len = %p, value of len = %d\n", &s->len, s->len); printf("addr of b = %p, value of b = %d\n", &s->b, s->b); printf("addr of s = %p, value of s = %s\n", &s->s, s->s); return 0; }
执行结果为:
sizeof(struct str) = 8 abc sizeof(struct str) = 8 addr of len = 0x9831008, value of len = 100 addr of b = 0x983100c, value of b = 50 addr of s = 0x9831010, value of s = abc
可以看出s确实是排在成员b的后面!尽管表面上看s是结构体str的成员,但实际上并不是;这样做的好处就是可以一次性将所需的内存空间申请出来(所以内存是连续的,内存碎片也减少了),因为如果是以指针形式声明的,那么就必须做两次内存申请(结构体一次,成员s一次)。
再看看成员char s[0]放在结构体中间位置处:
#include <stdio.h> #include <string.h> #include <stdlib.h> struct str { int len; char s[0]; int b; }; int main(int argc, char *argv[]) { struct str *s = NULL; printf("sizeof(struct str) = %d\n", sizeof(struct str)); s = (str *)malloc(sizeof(struct str) + 10); s->len = 100; s->b = 50; strcpy(&s->s[0], "abc"); puts(s->s); printf("sizeof(struct str) = %d\n", sizeof(struct str)); printf("addr of len = %p, value of len = %d\n", &s->len, s->len); printf("addr of s = %p, value of s = %s\n", &s->s, s->s); printf("addr of b = %p, value of b = %d\n", &s->b, s->b); return 0; }
执行结果为:
sizeof(struct str) = 8 abc sizeof(struct str) = 8 addr of len = 0x8da6008, value of len = 100 addr of s = 0x8da600c, value of s = abc addr of b = 0x8da600c, value of b = 6513249
可以清楚的看出s和b的地址是一样的,对s赋值,结果将b的值给覆盖重写了,有点像联合体哈!