有时我们需要产生一个结构体,实现了一种可变长度的结构。如何来实现呢?
看这个结构体的定义:
typedef struct st_type
{
int nCnt;
int item[0];
}type_a;
(有些编译器会报错无法编译可以改成:)
typedef struct st_type
{
int nCnt;
int item[];
}type_a;
这样我们就可以定义一个可变长的结构,用sizeof(type_a)得到的只有4,就是sizeof(nCnt)=sizeof(int)那个0个元素的数组没有占用空间,而后我们可以进行变长操作了。

优点:
1.不需要初始化,数组名直接就是所在的偏移 
2.不占任何空间,指针需要占用int长度空间,空数组不占任何空间。
“这个数组不占用任何内存”,意味着这样的结构节省空间;“该数组的内存地址就和他后面的元素的地址相同”,意味着无需初始化,数组名就是后面元素的地址,直接就能当做指针使用。
C语言版:
type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));
C 语言版:
type_a *p = (type_a*)new char[sizeof(type_a) +100*sizeof(int)];
这样我们就产生了一个长为100的type_a类型的东西用p->item[n]就能简单地访问可变长元素,原理十分简单

,分配了比sizeof(type_a)多的内存后int item[];就有了其意义了,它指向的是int nCnt;后面的内容,是没

有内存需要的,而在分配时多分配的内存就可以由其来操控,是个十分好用的技巧。
而释放同样简单:
C语言版:
free(p);
C 语言版:
delete []p;
一次分配解决问题,省了不少麻烦。大家知道为了防止内存泄漏,如果是分两次分配(结构体和缓冲区),那么要是第二次malloc失败了,必须回滚释放第一个分配的结构体。这样带来了编码麻烦。其次,分配了第二个缓冲区以后,如果结构里面用的是指针,还要为这个指针赋值。同样,在free这个p的时候,用指针也要两次free。如果用空数组,所有问题一次解决。

 

 Variable length arrays 是C99的特性,而不是 C++98 的,关于c99标准的变长数组, 在标准的6.7.5.2 Array declarators里面有这样的说明:  
    2.Only ordinary identifiers (as defined in 6.2.3) with both block scope or function prototype scope and no linkage shall have a variably modified type. If an identifier is declared to be an object with static storage duration, it shall not have a variable length array type.  
    换而言之, 变长数组不能是在静态存储区(包括全局变量和静态变量)中的。
    另外,VLA 需要支持 sizeof 运算, 动态sizeof 也是C99的一个特有特性。
    目前很多C++编译器尚不能支持动态数组特性(VC++2005不支持此特性, GCC3.2之后支持,之前的版本没有调查,不知道是从哪个版本开始支持此特性的)。gcc的文档里面说:Variable-length automatic arrays are allowed in ISO C99, and as an extension GCC accepts them in C89 mode and in C++。所以,使用GCC,即使你打开 -ansi (等价于 --std=c89)选项,也仍然可以使用动态数组。
    其实C99的VLA也不是真正意义上的变长数组—它只是在运行时可以根据一个变量生成一个数组,此数组此后并不会变化,而真正意义的变长数组可以在实际使用时可以动态伸缩。
    过去的C 和现在的C++中,自动变量的偏移都是预定的,但是VLA不能这样实现。
    下面的例子中,由于b, c大小不确定,所以它们的地址必须是运行时确定的

    void test(int tmp)                                                                                                                              
    {                                                                                                                                                          
            int a;                                                                                                                                           
                                
            scanf("%d", &a);                                                                                                                        
            int b[tmp];                                                                                                                                   
            int c[a];                                                                                                                                        
            printf ("size(b) = %d, size(c) = %d\n", sizeof(b)/sizeof(int), 
sizeof(c)/sizeof(int) );         
    }                                                                                                                                                     


    VLA在栈上分配空间,这个速度很快。 我们举一个典型的32位编译器下函数实现为例子。这个实现中,下面两个寄存器关注函数堆栈:

    ebp: 函数占有内容起点即帧(frame)的位置, esp:当前栈顶

    一般来说,一个函数中自动变量个数和大小都是确定的。比如共有4个int,一个大小为10的 float 数组。 编译器可以简单的 esp -= 56 就留出所有空间(以32位PC机为例)。 每个变量相对 ebp 的偏移也是确定的,所以,若他访问 int a,

        $a = 4, 他会这样访问: $a [ ebp ]

    对于 VLA 来说,事情复杂一些。 现在有一个 VLA c。 他的大小是不确定的 —— 所以它的偏移也是不确定的:如果它之前有另外一个 VLA b, 它不可能知道自己排到哪里。目前 GCC 中的做法是,在可变数组较少的情况下,直接用寄存器+偏移的方式来存取;在较多的情况下,都是先从堆栈中得到数组的起始地址, 然后再用偏移量的方式存取数组的元素。

    这个操作是非常快的。不过有一个问题是,栈的大小是固定的,而且不大。你不能分配很大的东西。另外这个数组是临时的 —— 一旦退出当前作用域,他就可以被任何其他函数刷掉。

    变长数组的特性综述(摘自[2]):

    1. 变长数组是分配在堆栈上的, 其实从语义的角度也应该是这样, 变长数组还是一个数组, 还是一个局部变量, 在c语言中, 局部变量是分配在堆栈上的, malloc才是分配在堆上面的. 这里面没有不存在什么内存泄漏, 因为堆栈上的内存是不需要程序员管理的  
   
    2. 变长数组和其他变量共同存在于一个作用域之内时, 变长数组是在最后分配的, 也就是处在堆栈的最下面(地址最低). 这也是变长数组不同于普通变量的情况, 普通变量的内存分配是按照定义顺序来的, 先定义的先分配, 但是变长数组不是这样的, 在一个作用域内变长数组都是放到最后分配的, 原因很简单, 变长数组的大小在编译时无 法确定, 所以如果在变长数组后面分配普通变量的空间, 那么对后面普通变量的存取就不方便了,当然简单的情况可以通过优化解决, 但是如果有很多的变量数组和普通变量混合在一起的话,优化很难做到很好, 所以把变长数组都放在最后面分配, 是一个比较合理的办法;  
    当然, 不是说一定要放在最后, 按照普通的定义顺序来分配空间也是可以的,只是在存取可变数组后面的变量的时候, 需要首先找到可变数组的起始地址, 然后再往下(低地址) 找到后面变量的地址, 存取相应的变量。目前gcc所使用的放到最后的做法, 也可以理解成一种优化. 。
   
    3. 对变长数组的元素存取, 可以把首地址放在寄存器里面, 也可以在把首地址放在堆栈里面,然后存取元素的时候先取出首地址, 然后在通过偏移量的方式存取里面的元素.  
   
    4.变长数组不能在静态存储区中, 这个很显然, 静态存储区在编译时就确定分配内存的大小,不像堆栈一样是动态的, 变长数组显然不能定义在静态存储区中.

    5. 变长数组的意义, 我觉得在c里面还是有一些意义, 如果是在对性能,内存要求十分严格的地方, 允许声明变长数组还是比较方便的, 因为原来需要一块连续内存的地方要么声明的大一些,保证肯定够用, 但这样就浪费了, 在对内存要求严格的地方就不好了, 另外一种做法就是malloc动态分配, 但是这样的缺点是需要手动管理, 要手动free, 程序如果大的话内存管理不好的话容易内存泄漏,所以在c里面还是有一些意义. 但是在c++里面已经有vector了, 而且c++也不太多应用在对性能,内存要求非常高的地方,  
   
    6.使用变长数组, 最重要的当然是要检查数组的size的合法性了, 我前面举例为了减少干扰因素, 都没有做检查, 实际的程序肯定要做这个检查, 则面试的时候这样的程序肯定立刻被kick. 另外, 就是又多了一种堆栈溢出的方法,数组大小如果是负的话又可以跳到不该跳到的地方了