C语言中如何使用动态数组而不用malloc?

21

我一直想知道如何逃脱这件事:

int main(int argc, char **argv) {
    printf("%p %s %d\n", &argv[1], argv[1], strlen(argv[1]));
    char copy[strlen(argv[1]) + 1];
    strcpy(copy, argv[1]);
    printf("%p %s %d\n", &copy, copy, strlen(copy));
    return 0;
}

字符数组copy被分配了内存并且程序正常运行,打印出原始数组和复制数组。Valgrind也没有报错。

我曾认为在C语言中,没有使用malloc是不可能实现动态数组的。这个想法是错误的吗?


3
"dynamic"意味着可调整大小,不一定是“编译时常量”。这是C99引入的。 - Chris Lutz
1
如果 strlen(argv[1]) 足够大,你可能无法逃脱它! - David Heffernan
4个回答

19

这是C99的一个特性,编译器可以在之前的版本上实现

ISO C99允许使用可变长度自动数组,在C90模式和C++中,GCC作为扩展接受它们。这些数组像其他自动数组一样声明,但长度不是常量表达式。存储在声明点分配,当大括号层级退出时释放。


7
变长数组最初起源于GCC扩展,但它们也被C99采用。
它们仍然在堆栈上分配,因此将它们设置为“巨大”被认为是不好的风格(并且有一天可能会出问题)。

哇,我已经很久没有使用纯C(或gcc)了,以至于我完全错过了这个成为标准的事情。 - Dima
@Dima:说实话,直到我重新阅读GCC手册中的描述,我才想起来自己忘了这个功能。 (我从不使用此功能;动态堆栈使用太危险了。) - Nemo
@Nemo:只要你知道目标平台的堆栈限制并加以考虑,我认为这并不危险。 - Stephen Canon
1
@Stephen:根据我的经验,代码总是比“目标平台”更加持久。换句话说,最终的目标平台永远不会像你想象的那样可靠。此外,有一些优秀的静态检查工具(如Coverity),可以计算程序总堆栈消耗的上限,但可能无法在存在这种情况的情况下进行。因此,总的来说,我认为最好避免使用这个小工具。(当然,每个规则都有例外。) - Nemo
我同意你们两个的观点;我经常使用递归来实现简单和方便的解决方案,但是大量不确定的堆栈模式确实需要额外考虑。 - Taliadon

3

即使在“变长数组”(由gcc和C99提供)出现之前,已经有了:

alloca() -- 它允许动态分配堆栈(“自动”)内存。


根据BSD中关于alloca的man页面,它是在AT&T Unix Version 32V(1979年)中引入的。因此,它已经存在了一段时间。 - ldav1s
当分配可能大量内存时,alloca是有害的。 - Vorac
1
@Vorac - 感谢你的建议,但那并不准确。你亲自尝试过吗?程序通常可以获得的栈内存远远超过“几百字节”。显然,我们始终需要注意机器的限制,但假设alloca()比sbrk()更受限制这种说法实在不合乎逻辑。 - Heath Hunnicutt

2
"C99"在C语言中添加了"可变长度数组"。这在§6.7.5.2 "数组声明符"中有所涵盖:
如果大小是一个不是整数常量表达式的表达式:如果它出现在函数原型作用域的声明中,则被视为被*替换;否则,每次评估时它必须具有大于零的值。可变长度数组类型的每个实例的大小在其生命周期内不会改变。当大小表达式是sizeof运算符的操作数的一部分,并且更改大小表达式的值不会影响运算符的结果时,未指定是否评估大小表达式。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接