0长度数组的使用

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指向的内容。

    因此,可以看出其中的差别:数组的原地就是内容,而指针的那里保存的是内容的地址。


 

posted on 2014-07-03 14:48  eric.geoffrey  阅读(626)  评论(0编辑  收藏  举报

导航