【发布时间】:2021-07-11 12:25:09
【问题描述】:
我确信这已被问过一百万次,但我找不到解释它的答案。有人告诉我永远不要这样做,但我真的不明白为什么。为什么这样的事情不能算作动态分配内存,为什么这么糟糕?
int a;
scanf("%d",&a);
int arr[a];
【问题讨论】:
-
这不是 [] 运算符,它被称为变长数组
标签: c dynamic-memory-allocation
我确信这已被问过一百万次,但我找不到解释它的答案。有人告诉我永远不要这样做,但我真的不明白为什么。为什么这样的事情不能算作动态分配内存,为什么这么糟糕?
int a;
scanf("%d",&a);
int arr[a];
【问题讨论】:
标签: c dynamic-memory-allocation
这不是动态分配,而是一个可变长度数组。
这样一个数组的生命周期是它的封闭范围,就像一个固定大小的数组,所以你不需要担心释放。这些数组通常驻留在堆栈上,因此堆栈的大小确实限制了 VLA(或任何数组)的大小。
如果您发现 VLA 对堆栈来说太大,您可以随时回退到使用 malloc。
【讨论】:
使用 [] 运算符而不是使用 malloc 动态分配数组?
这里的[] 用于定义一个数组。 []这里,不是运营商。
int arr[a];
有人告诉我永远不要这样做,但我还没有真正理解为什么。
这句话适用于 C(和生活)中的许多事情,在您了解如何使用之前不要使用它。
为什么这样的事情不能算作动态分配内存
它是动态分配内存分配,但通常使用不同的内存池并且具有与通过malloc()不同的生命周期。
...为什么它那么糟糕?
int arr[a]; 是一个变长数组 (VLA),存在以下问题:
并非总是可用。 VLA 在 C99 中支持,并且在以后的版本中经常(但并非总是)支持。研究__STDC_NO_VLA__.
对于int a,代码在a <= 0 时是未定义行为(UB)。
当a 太大时,没有标准机制来检测内存不足。
VLA 更好地用于受控的较小尺寸的情况。
【讨论】:
首先,让我告诉你,这叫做变长数组,它是一个可选功能。最好不要依赖此功能(或编译器支持)。
另一方面,分配器函数(malloc()、free() 和系列)符合标准,任何符合标准的编译器/库都将支持这些函数。
也就是说,other answers 中提到了区别,主要区别在于范围和可移植性。
【讨论】:
为什么这样的事情不能算作动态分配内存
对象的大小是在运行时确定的,不足以进行动态分配。动态分配需要直接或间接使用专门执行动态内存分配的函数之一,malloc、calloc 和realloc 是主要示例。你的例子...
int arr[a];
... 不这样做,因此对象不是动态分配的。你arr是一个或多或少的普通局部变量,具有变长数组类型。
从语义上讲,动态分配的对象具有“已分配”的存储持续时间,这意味着它们存在并保留其最后存储的值,直到显式解除分配。您的 arr 具有“自动”存储持续时间,这意味着当其最内层包含块的执行终止时,它不再存在。
为什么这么糟糕?
意见不一。 VLA 在 C99 中是必需的功能,但在 C11 中支持是可选的。如果您在代码中使用 VLA,那么您会限制其可移植性。 C++ 不支持它们(或者至少不支持——我知道它们现在也开始使用 C++,而且也许它们已经出现了),这对某些人来说可能是个问题。
然而,引用的主要风险是 VLA 的常规和自然实现是在堆栈上分配它们,而堆栈大小通常比堆空间更有限。在像您这样的幼稚示例中,很容易创建堆栈溢出,这可能取决于用户输入,因此既难以测试又存在潜在的安全风险。
我确实不时在我的代码中使用 VLA,但通常以不受上述堆栈破坏风险影响的方式使用。我通常对“永远不要这样做”的告诫持怀疑态度,但实际上我永远不会编写您为生产应用程序提供的确切代码。
【讨论】:
这是一个可变长度数组的例子。它的生命周期与任何其他 auto 变量(包括定长数组)相同,因此一旦退出其封闭范围,它的内存就会被释放。
与固定长度数组不同,VLA 不能在文件范围(任何函数之外)或使用 static 关键字声明,也不能使用初始化程序声明。由于它们通常是如何管理的,它们不能任意大。尽管有名称,但一旦定义它们就无法调整大小 - 可变长度的“变量”仅意味着它们的大小在每次定义时都可以不同。
VLA 有用但有限。当您需要一些在函数本地的临时工作存储并且不需要太大(不超过 1 兆字节左右)时,它们非常有用。
它们仅在 C99 中引入,在 C11 中成为可选,因此对它们的支持可能有点参差不齐。
【讨论】: