可变长度数组分析说明

1、零长度数组说明

         长度为的数组在标准c和c++中是不允许的,如果使用长度为的数组,编译时会产生错误,提示数组长度不能为。但在GNUc中,这种用法却是合法的。它的最典型的用法就是位于数组中的最后一项,如上面所示,这样做主要是为了方便内存缓冲区的管理。如果你将上面的长度为的数组换为指针,那么在分配内存时,需采用两步:首先,需为结构体分配一块内存空间;其次再为结构体中的成员变量分配内存空间。这样两次分配的内存是不连续的,需要分别对其进行管理。当使用长度为的数组时,则是采用一次分配的原则,一次性将所需的内存全部分配给它。相反,释放时也是一样的。

2、零长度代码示例

对于长度为的数组,在gcc手册中,有如下一段代码片段:

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

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

这段代码的主要含义是定义了一个结构体,并对其进行初始化。当采用malloc为其申请内存空间时,如上所示,申请了一段长度为结构体长度加可变长度的内存空间给结构体类型的指针,这时contents就指向申请的可变长度的内存空间。由于是一次申请的,所以这段可变长度的内存空间和前面的结构体长度的内存空间是连续的。对于这段可变长度的内存空间,可以采用数组的方式对其进行访问。对于整个结构体,当不再使用时,可以使用free函数一次性对其进行释放,而不必像指针那样分别释放。

3、零长度数组测试代码

测试代码如下:

#include <stdio.h>
#include <stdlib.h>
#define LENGTH 10

struct test1
{
    int a;
    int *b;
}__attribute((packed));

struct test2
{
    int a;
    int b[0];
}__attribute((packed));

struct test3
{
    int a;
    int b[2];
}__attribute((packed));

int main()
{
    struct test1 *var1;
    struct test2 *var2;
    struct test3 *var3;
    int i = 0;

    printf("the length of struct test1:%d\n",sizeof(struct test1));
    printf("the length of struct test2:%d\n",sizeof(struct test2));
    printf("the length of struct test3:%d\n",sizeof(struct test3));

    var1=(struct test1*)malloc(sizeof(struct test1));
    var1->a=1;
    var1->b=(int *)malloc(sizeof(int));
    *var1->b=1;
    printf("\nvar1->a=%d,*(var1->b)=%d\n",var1->a,*var1->b);

    var2=(struct test2*)malloc(sizeof(struct test2)+sizeof(int)*LENGTH);
    var2->a=2;
    printf("\nvar2->a=%d\n",var2->a);
    for(i=0;i<LENGTH;i++)
    {
        var2->b[i]=i;
        printf("var2->b[%d]=%d\r\n",i,var2->b[i]);
    }
    printf("\n\n");

    var3=(struct test3*)malloc(sizeof(struct test3));
    var3->a=3;
    (var3->b)[0]=3;
    (var3->b)[1]=4;
    printf("var3->a=%d,(var3->b)[0]=%d, (var3->b)[1]=%d\n",var3->a,(var3->b)[0], (var3->b)[1]);


    free(var1->b);
    free(var1);
    free(var2);
    free(var3);

    return 0;
}

测试结果如下:

4、相关说明

  1. 尽管零长度数组的大小为零,但由于尾部填充,此类数组成员可能会增加封装类型的大小。零长度数组成员导致的偏移量与具有一个或多个相同类型元素的数组的偏移量相同。零长度数组的对齐与有元素的数组对齐方式相同。
  2. 不鼓励在其他上下文中声明零长度数组,包括作为结构对象的内部成员或作为非成员对象。访问在这种上下文中声明的零长度数组的元素是未定义的,可能被编译器检测出来。
  3. 在没有长度为零的数组扩展的情况下,在ISO C90中,上面示例中的内容数组通常被声明为只有一个元素。不像zerolength数组,它只决定封闭结构的大小以达到对齐的目的,一个元素数组总是占用至少与该类型的单个对象相同的空间。虽然不鼓励使用单元素数组,但是GCC可以处理访问以类似的方式尾随单元素数组成员到零长度数组。
  4. 在ISO C99声明可变长度类型(如上面的struct line)的首选机制是灵活数组成员,语法和语义略有不同:
    •灵活的数组成员被写为内容[],没有0。
    •灵活的数组成员具有不完整的类型,因此sizeof操作符可能不会被应用。作为零长度数组的原始实现的一个奇怪之处,sizeof计算结果为0。
    •灵活数组成员只能作为结构体的最后一个成员出现,否则非空。
    •包含灵活数组成员的结构,或包含此类结构(可能是递归的)的联合,可能不是结构或数组的成员。(但是,GCC允许这些使用作为扩展。)
    初始化效果相同
    struct f1 {
        int x; int y[];
    } f1 = { 1, { 2, 3, 4 } };
    struct f2 {
        struct f1 f1; int data[3];
    } f2 = { { 1 }, { 2, 3, 4 } };

     

  5. GCC允许C结构没有成员:

    struct empty {
    };

    结构的大小为零。在c++中,空结构是语言的一部分。G + +将空结构视为具有char类型的单个成员。

5、可变长数组

ISO C99允许可变长度的自动数组,作为GCC的扩展,在C90模式和c++中也支持这种特性。这些数组的声明与任何其他自动数组一样,但其长度不是常量表达式。数据的存储空间在声明处被分配,当包含声明的块作用域退出时,释放存储空间。例如:

FILE *
concat_fopen (char *s1, char *s2, char *mode)
{
    char str[strlen (s1) + strlen (s2) + 1];
    strcpy (str, s1);
    strcat (str, s2);
    return fopen (str, mode);
}

 跳转出数组名称的作用域后,会释放存储空间。不允许跳入数组名的作用域。

  • 作为扩展,GCC接受可变长度数组作为结构或联合的成员。例如:
void
foo (int n)
{
    struct S { int x[n]; };
}

可以使用函数alloca来获得类似于可变长度数组的效果。alloca函数在许多其他C实现中都可用(但不是全部)。另一方面,可变长度数组更优雅。

  • 使用可变长度数组作为函数的参数:
struct entry
tester (int len, char data[len][len])
{
    /* . . . */
}

数组的长度在分配存储时计算一次,并记住数组的范围,便于通过sizeof访问数组长度。如果希望先传递数组,然后再传递长度,可以在参数列表中使用前向声明--另一个GNU扩展。

struct entry
tester (int len; char data[len][len], int len)
{
    /* . . . */
}

分号前的' int len '是一个参数前向声明,它的目的是在解析数据声明时,使名称len能够正常解析。

可以在参数列表中写入任意数量的此类参数前向声明。它们可以用逗号或分号分隔,但最后一个必须以分号结束,分号后面跟着“真实的”参数声明。每个前向声明必须在参数名和数据类型上匹配“真实”声明。ISO C99不支持参数前向声明。

借鉴资料:

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值