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

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

疑问

       为什么不使用指针来代替零长度数组:

大家在各种场合,可能常常会看到这样的字眼:数组名在作为函数参数进行参数传递时,就相当于是一个指针。在这里,我们千万别被这句话迷惑了:数组名在作为函数参数传递时,确实传递的是一个地址,但数组名绝不是指针,两者不是同一个东西。数组名用来表征一块连续内存存储空间的地址,而指针是一个变量,编译器要给它单独再分配一个内存空间,用来存放它指向的变量的地址。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值