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

Posted

技术标签:

【中文标题】C中堆栈上的动态数组分配【英文标题】:Dynamic array allocation on stack in C 【发布时间】:2014-12-14 00:49:39 【问题描述】:

我昨天刚做了一个实验,发现有些混乱:

#include <stdio.h>

int main()

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

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

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

【问题讨论】:

事实上你应该澄清为什么你认为编译器应该给出一个错误 在本站搜索[c] VLA 我想你一直在学习c89.. 作为旁注,通过附加变量i 是没用的,没有代码也可以。 由于添加了 C11 标签,请注意 C11 引入的 VLA 支持可选。 __STDC_NO_VLA__ 类对象宏设置为 1 如果具体实现不处理它们。 【参考方案1】:

可变长度数组已添加到 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_localstaticextern 声明的数组对象 存储类说明符不能有可变长度数组 (VLA) 类型。但是,使用 static 存储类声明的对象 说明符可以具有 VM 类型(即,指向 VLA 类型的指针)。 最后,所有使用 VM 类型声明的标识符都必须是普通的 标识符,因此不能是结构或联合的成员。

这意味着静态存储和自动存储是互斥的。

【讨论】:

嗨,Remyabel,感谢您的回复。在这种情况下,编译器可能会生成代码,这些代码将使用变量而不是固定数字来修改堆栈指针,我的猜测是否正确? 我同意“堆栈上的动态数组分配”这句话的措辞非常糟糕,但它是运行时的,但它使用了我们习惯的语法,过去是编译时声明。更新答案说允许运行时数组分配(指定长度的运行时)比说不允许动态数组分配更好。【参考方案2】:

要深入研究如何在堆栈上分配可变数量的内存,请参阅深入研究编译器如何实现 (non-standardized) alloca() 函数:

Alloca implementation

C99 标准提供的Variable Length Arrays ("VLA") 具有基本相同的功能;尽管内存是按作用域而不是按功能回收的:

What's the difference between alloca(n) and char x[n]?

有一些理由会犹豫是否过于激进地使用无限制的大小。无法检查堆栈内存是否可用,因为您可以通过以下方式测试堆内存是否可用。来自 malloc() 的 NULL。如果你的变长数组太大会导致堆栈溢出和undefined behavior;两种堆栈分配方法都为真:

Why is the use of alloca() not considered good practice?

【讨论】:

【参考方案3】:

C 具有可变长度数组这样的特性。可以在飞行中定义自动存储时长的数组。

【讨论】:

以上是关于C中堆栈上的动态数组分配的主要内容,如果未能解决你的问题,请参考以下文章

C ++在函数内部分配动态数组[关闭]

为什么我们不能在堆栈上分配动态内存?

堆栈动态和堆栈动态数组

动态内存分配

怎么动态分配指针数组

C++ 优化 - 堆栈分配的数组类型与外部链接维度?