C语言中堆栈上的动态数组分配

10

我昨天刚刚做完一个实验,发现了一些令人困惑的事情:

#include <stdio.h>

int main()
{
    int j;
    scanf("%d",&j);
    const int i = j;
    int arr[i];
    return 0;
}

从键盘读取数字 j,然后用它在堆栈上分配数组 arr

编译器甚至不知道数组的大小(将 j 初始化为 0?),但没有编译错误。这怎么可能呢?


3
事实上,您应该阐明为什么认为编译器应该报错。 - m0skit0
6
请在此网站中搜索“[c] VLA”。 - BLUEPIXY
我认为你一直在学习C89。 - Haris
附带说明一下,传递额外变量i是没有必要的,代码在没有它的情况下也可以正常运行。 - Jens Gustedt
显示剩余2条评论
3个回答

15
变长数组是在C99中添加的。在C99的解释中进行了描述:
6.7.5.2 数组声明符
引用如下:
C99增加了一种新的数组类型,称为变长数组类型。无法声明仅在执行时才知道大小的数组,这经常被认为是使用C作为数值计算语言的主要障碍。采用某些标准的执行时间数组概念被认为对于C在数值计算世界中的接受至关重要。
在变长数组类型的声明中指定的元素数量是运行时表达式。在C99之前,这个大小表达式必须是整数常量表达式。
没有"堆栈上的动态数组分配"。数组大小必须在声明时指定。
一些编译器,如GCC在C90(在GCC中,这相当于ansi和C89)模式和C++中允许它们作为扩展。在这些情况下,您将收到警告(-Wpedantic)或错误(-Werror-pedantic-errors)。请查阅您的编译器文档。
根据@Deduplicator的评论,您似乎有一个误解。可变长度数组不能声明为静态的。
§ 6.7.6.2
10 EXAMPLE 4 所有可变修饰(VM)类型的声明都必须在块作用域或函数原型作用域中。使用_Thread_local、static或extern存储类说明符声明的数组对象不能具有可变长度数组(VLA)类型。但是,使用static存储类说明符声明的对象可以具有VM类型(即指向VLA类型的指针)。最后,所有使用VM类型声明的标识符都必须是普通标识符,因此不能是结构体或联合体的成员。
这意味着静态存储和自动存储是互斥的。

嗨,Remyabel,谢谢您的回复。在这种情况下,编译器可能会生成代码,用一个变量而不是一个固定数来修改堆栈指针,我的猜测正确吗? - user3769509
我同意“堆栈上的动态数组分配”这个短语用词非常不当,但它是在运行时进行的,但它使用了我们习惯的语法,这曾经是编译时声明。更新答案以表明允许运行时数组分配(指定长度运行时)比说动态数组分配不允许更好。 - Brian Bulkowski

3

如果您想深入了解在堆栈上分配变量内存的工作原理,请参阅这篇关于编译器如何实现(非标准)alloca()函数的文章:

Alloca implementation

C99标准提供了可变长度数组("VLA"),并具有基本相同的功能;尽管内存是按作用域而不是按函数基础回收的:

alloca(n)和char x[n]之间有什么区别?

使用不受限制的大小时,有一些原因会让人犹豫不决。无法像通过malloc()测试堆内存是否可用那样检查堆栈内存是否可用。如果您的变长数组太大,它将导致堆栈溢出和未定义行为;对于堆栈分配的两种方法都是如此。

为什么不建议使用alloca()?


0

C语言有一个叫做"变长数组(Variable Length Arrays)"的特性,它可以让我们在运行时动态定义自动存储期的数组。


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