【发布时间】:2018-03-05 08:31:14
【问题描述】:
我编写了一个 C 程序,它接受来自用户的整数输入,用作整数数组的大小,并使用该值声明一个给定大小的数组,我通过检查大小来确认它数组。
代码:
#include <stdio.h>
int main(int argc, char const *argv[])
{
int n;
scanf("%d",&n);
int k[n];
printf("%ld",sizeof(k));
return 0;
}
令人惊讶的是它是正确的!该程序能够创建所需大小的数组。
但是所有的静态内存分配都是在编译时完成的,而在编译时n的值是未知的,那么编译器为什么能够分配所需大小的内存呢?
如果我们可以像这样分配所需的内存,那么使用malloc() 和calloc() 进行动态分配有什么用?
【问题讨论】:
-
为什么要这样做,而不是正常的“k = (int *) calloc (n, sizeof (int));”?只是为了混淆你的代码?
-
@jamesqf
int k[n];是k = (int *) calloc (n, sizeof (int));的混淆版本吗?我认为前者更具可读性(如果您知道存在 VLA)。 -
@jamesqf:性能。将
n加载到rsi(准备成为 x86-64 SysV ABI 中 printf 的第二个参数)后,sub rsp, rsi(一个简单的 asm 指令)比函数便宜得多 -致电calloc。虽然在这种情况下,k[]本身并没有被使用,只有sizeof(k),所以一个好的编译器不会在调用printf之前实际保留堆栈空间。堆栈内存在 L1D 缓存和 TLB 中已经很热,因此它是小缓冲区的好地方。发布它也非常便宜,而且你不可能每个人都会出错,因为编译器会为你做。 -
@jamesqf:它不会检查大小,也不会优雅地失败。程序员不能编写使用对于他们关心的实现来说太大的 VLA 的程序。 (例如8MB stack size in new user-space threads on Linux x86-64)。通常,如果您触摸堆栈底部下方的内存并且操作系统认为这太多并且不会增加您的堆栈映射,那么您会出现段错误。在非叶函数中使用大型 VLA 与可能也使用 VLA 的子函数一起使用是个坏主意。
-
@jamesqf:这听起来比
new/delete差很多,但是对于过度使用内存的现代操作系统,情况几乎没有更糟。您可以分配比操作系统拥有的物理 RAM + 交换空间更多的 RAM,并且触摸它可能会导致内核决定终止您的进程。 (Linux 称之为 OOM 杀手)。 linuxdevcenter.com/pub/a/linux/2006/11/30/…。但是,您可以通过设置进程可以分配的虚拟内存量限制来优雅地失败分配,因此malloc实际上会返回 NULL,但这不是默认值。
标签: c arrays memory c99 variable-length-array