0长度的数组在ISO C和C++的规格说明书中是不允许的,但是GCC的C99支持的这种用法。
GCC对0长度数组的文档参考:“Arrays of Length Zero”
如下代码片段,哪个更简洁更灵活,看一眼就知道了:
#include <stdlib.h>
#include <string.h>
typedef struct tagArray
{
int length;
char contents[]; //这个成员必须是结构体的最后一个成员。
}ARRAY_S;
typedef struct tagPointer
{
int length;
char *contents;
}POINTER_S;
int main()
{
int array_length = 10;
ARRAY_S *pArray = (ARRAY_S*)malloc (sizeof(ARRAY_S) + array_length);
POINTER_S *pPointer = NULL;
if (NULL == pArray)
{
return 0;
}
pArray->length = array_length;
memset(pArray->contents, 'a', array_length);
free(pArray);
pPointer = (POINTER_S*)malloc(sizeof(POINTER_S));
if(pPointer == NULL)
{
return 0;
}
memset(pPointer, 0, sizeof(POINTER_S));
pPointer->length = array_length;
pPointer->contents = (char*)malloc(array_length);
if (pPointer->contents == NULL)
{
free(pPointer);
return 0;
}
memset(pPointer->contents, 'a', array_length);
free(pPointer->contents);
free(pPointer);
return 0;
}
第一种结构体的定义:想给一个结构体内的数据分配一个连续的内存,有两个好处:
(1)方便内存释放。
如果我们的代码提供给别人使用,你在里面做了二次内存分配,并把整个结构体返回给用户。
用户调用free可以释放结构体,但是用户并不知道这个结构体内的成员也需要free,所以你不能指望用户来发现这个事。
所以,如果我们把结构体的内存以及其成员要的内存一次性分配好了,并返回给用户一个结构体指针,用户做一次free就可以把所有的内存也给释放掉。
(2)有利于访问速度。
连续的内存有益于提高访问速度,也有益于减少内存碎片。
第二种结构体的使用:需要分配两次内存以及释放两次内存,在检查申请内存成功与否的代码量上看也明显没有第一种更简洁。
看看内存是怎么个连续的,用gdb的x命令来查看:(ARRAY_S中的那个char contents[]不占用结构体的内存,
所以ARRAY_S就只有一个int成员,4个字节,而我们还要为contents[]分配10个字节长度,所以,一共是14个字节):
1 (gdb) p pArray
2 $1 = (ARRAY_S *) 0x804b008
3 (gdb) p *pArray
4 $2 = {length = 10, contents = 0x804b00c "aaaaaaaaaa"}
5 (gdb) p pArray ->contents
6 $3 = 0x804b00c "aaaaaaaaaa"
7 (gdb) x/14b pArray
8 0x804b008: 10 0 0 0 97 97 97 97
9 0x804b010: 97 97 97 97 97 97
从上面的内存布局我们可以看到,前4个字节是 int length,后10个字节就是char contents[]。
如果用指针的话,会变成这个样子:
10 (gdb) p pPointer
11 $4 = (POINTER_S *) 0x804b020
12 (gdb) p *pPointer
13 $5 = {length = 10, contents = 0x804b030 "aaaaaaaaaa"}
14 (gdb) p pPointer ->contents
15 $6 = 0x804b030 "aaaaaaaaaa"
16 (gdb) x/16b pPointer
17 0x804b020: 10 0 0 0 48 -80 4 8
18 0x804b028: 0 0 0 0 17 0 0 0
19 (gdb) x/10b pPointer ->contents
20 0x804b030: 97 97 97 97 97 97 97 97
21 0x804b038: 97 97
22 (gdb) x/16x pPointer
23 0x804b020: 0x0a 0x00 0x00 0x00 0x30 0xb0 0x04 0x08
24 0x804b028: 0x00 0x00 0x00 0x00 0x11 0x00 0x00 0x00
第17行前四个字节是 int length,后四个字节是contents的地址。
第23行以16进制显示,地址是: 0x30 0xb0 0x04 0x08, 即:0x0804b030。
第20行和第21行是char* contents指向的内容。
因此,可以看出其中的差别:数组的原地就是内容,而指针的那里保存的是内容的地址。
疑问
为什么不使用指针来代替零长度数组:
大家在各种场合,可能常常会看到这样的字眼:数组名在作为函数参数进行参数传递时,就相当于是一个指针。在这里,我们千万别被这句话迷惑了:数组名在作为函数参数传递时,确实传递的是一个地址,但数组名绝不是指针,两者不是同一个东西。数组名用来表征一块连续内存存储空间的地址,而指针是一个变量,编译器要给它单独再分配一个内存空间,用来存放它指向的变量的地址。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)