在C语言中,数组的大小是否可以在运行时确定?

27

据我所知,在 c 语言中,数组在编译时需要有一个特定的大小。

但是我不明白为什么这段代码仍然可以工作?

int s;
printf("enter the array size: ");
scanf("%d",&s);

int a[s]; // Isn't s value determined at run time?
6个回答

30

哦,我明白了。谢谢你。所以数组的所有元素仍然位于堆栈上? - user188276
是的,如果那样不好的话,请使用其他答案中建议的malloc函数。 - Keith Randall
4
链接失效了。 - Santropedro

12

可变长度数组C99以来就是C语言的一部分。 但在C11中它们被作为可选功能 - 这意味着符合C11的实现不需要提供它(尽管,实际上支持C99的所有实现都肯定在C11中提供VLAs)。

您可以使用宏__STDC_NO_VLA__来检查您的实现是否不支持VLAs(如果在C99或C11编译模式下定义,则您的实现不支持VLAs)。

因此,在现代C(>= C99)中可以在运行时决定数组大小,像下面这样的代码是可以的:

int s;
printf("Enter the array size: ");
scanf("%d", &s);
int a[s];

VLAs的一个明显缺点是,如果`s`很大并且分配`a`可能失败。更糟糕的是,没有办法检查分配是否失败,你将遇到运行时错误(例如segfault)。这本质上是未定义的行为。因此如果数组大小太大,则应避免使用VLAs。基本上,当存在疑虑时,请选择动态内存分配(请参见下文)。
与其他问题相比,VLAs的另一个问题要轻得多,它们具有自动存储期(也称为“堆栈分配”)。因此,如果您想要持续时间较长的东西,则无法使用VLA超出声明VLA的块作用域。
还要注意的是,在C89中没有VLA。因此,使用动态内存分配是唯一的方法。尽管有一些非标准扩展,如alloca(),它类似于VLA,并具有与VLAs相同的缺点)。
int s;
printf("enter the array size: ");
scanf("%d",&s);
int *a = malloc(s * sizeof *a);
...
free(a);

5
如果您需要分配一个动态大小的数组,您需要使用malloc()从堆中获取它。
int *a = malloc(sizeof(int) * s)

2
如果你去掉(int*)的强制类型转换,如果你忘记包含定义malloc的文件,编译器将会生成一个错误。因此最好的做法是不要包含这个强制类型转换。 - Mark Ransom
2
int *a = malloc(s * sizeof *a) - AnT stands with Russia

2

这段代码是符合C99语言规范的。同时,作为扩展功能,GCC编译器也支持在C89/90模式下编译该代码。

因此,对于你的问题(为什么它“能用”),答案取决于你如何进行编译。一般情况下,这段代码甚至无法被C89/90编译器编译。


1
你在这里混淆了两件事情。
1)确定已分配数组的大小(正如您的标题所示):将sizeof()除以总数,再除以一个元素的大小(比如第一个元素)。
 sizeof(a)/sizeof(a[0])

2) 根据您的问题动态分配内存:

 int *a = (int*)malloc( s * sizeof(int) );

1
sizeof(a)/sizeof(a[0]) 决定了在编译时数组的大小,如果它像数组声明一样在栈上分配内存,则不是在运行时确定大小。 - Levent Divilioglu

0

了解编译器如何分配变量的内存是回答您问题的关键。变量的内存分配有两种模式,可以在堆上分配,也可以在栈上分配。堆上的内存是动态分配的。因此,在堆上分配内存的变量可以在运行时给出其大小。

C语言中的数组在栈上分配内存。为了在栈上提供内存,编译器在编译时应该知道内存的大小。这样,在运行时就可以为变量在栈上设置足够的内存。这就是为什么在C语言中不能在运行时决定数组的大小的原因。


1
在堆栈上分配内存只是调整堆栈指针的问题 - 并且没有任何根本原因可以解释为什么堆栈指针只能通过编译时已知的值进行调整。事实上,最近的C标准允许在运行时确定大小的自动变量,并且还有一个常见的扩展alloca()在此之前多年就提供了相同的功能。 - caf

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