变长数组和alloca

C99标准里规定了允许在栈上定义变长数组或者叫变长自动数组(Variable-length Automatic Array). 例如:

/* s1 and s2 are two strings(char*) */
char str[strlen (s1) + strlen (s2) + 1]; /* str 就是栈上的变长数组 */
strcpy (str, s1);
strcat (str, s2);

栈上的变长数组比堆上的变长数组有什么好处呢?

1. 不需要担心内存回收,数组变量在超出作用域后会自动回收;
2. 对于多维数组使用尤为方便,例如:

int arr[m][n][k];
int ***p = (int***)malloc(sizeof(int) * m * n * k); /* 这样使用不如 arr 直观方便 */

gcc在这方面早就有了很好的支持,我实际试的是在3.x版就已经支持了,而且,对于C90和C++都支持。
另外,gcc还增加了一个函数:alloca()
类似malloc(),只是alloca()分配的内存是在栈上。

alloca() 和栈上的变长数组的区别在于,alloca() 的分配的内存有效范围是在整个函数内部,而变长数组的有效范围是 { } 之间,例如:

void func(void)
{
    int *p = NULL;
    if (xxx) {
        p = alloca(sizeof(int)); /* alloca分配的内存在if语句外也可以使用. */
        int arr[n]; /* arr的有效范围在if语句内. */
        ...
    }
    ...
}

不过需要注意的是,如果alloca和变长数组在同一个函数里混用时,当一个变长数组被销毁时,在他之后调用的alloca分配的内存也将被销毁。这是gcc manual中的原文:

If you use both variable-length arrays and alloca in the same function, deallocation of a variable-length array will also deallocate anything more recently allocated with alloca.

这点很好理解,变长数组和alloca都是在栈上分配内存,编译器在对栈上的变量销毁时只是简单的弹栈,如下:

if (xxx) {
   int arr[n];
   p = alloca(xxx);
}

上面这段代码执行时栈的变化:

      |   |       top-->|   |             |   |
top-->|   |             | p |             | p |已经无效了!
      |arr|             |arr|             |arr|弹栈,销毁arr
      |...|             |...|       top-->|...|
      +---+             +---+             +---+
(1)分配arr空间      (2)分配p的空间       (3)销毁arr时p同时被销毁

因此,虽然alloca和变长数组可以给我们带来方便,但使用时一定要小心!

你可能感兴趣的:(数组)