为什么C/C++编译器在编译时需要知道数组的大小?

Eri*_*c Z 37 c c++

我知道C99之前的C标准(以及C++)说堆栈上的数组大小必须在编译时知道.但那是为什么呢?堆栈上的数组在运行时分配.那么为什么大小在编译时很重要?希望有人向我解释编译器在编译时将如何处理大小.谢谢.

这种数组的例子是:

void func()
{
    /*Here "array" is a local variable on stack, its space is allocated
     *at run-time. Why does the compiler need know its size at compile-time?
     */
   int array[10]; 
}
Run Code Online (Sandbox Code Playgroud)

caf*_*caf 75

要理解为什么可变大小的数组实现起来更复杂,您需要了解一下通常如何实现自动存储持续时间("本地")变量.

局部变量往往存储在运行时堆栈中.堆栈基本上是一个大的内存数组,它按顺序分配给局部变量,并且单个索引指向当前的"高水位线".这个索引是堆栈指针.

当输入函数时,堆栈指针在一个方向上移动,以在堆栈上为局部变量分配内存; 当函数退出时,堆栈指针向另一个方向移回,以释放它们.

这意味着仅在参考函数入口1处的堆栈指针的值时定义内存中局部变量的实际位置.函数中的代码必须通过堆栈指针的偏移量访问局部变量.要使用的确切偏移量取决于局部变量的大小.

现在,当所有的局部变量具有固定在编译时的尺寸,从栈指针这些偏移也是固定的 - 这样他们就可以直接编码到编译器发出指令.例如,在此函数中:

void foo(void)
{
    int a;
    char b[10];
    int c;
Run Code Online (Sandbox Code Playgroud)

a可能被访问STACK_POINTER + 0,b可能被访问STACK_POINTER + 4,并c可能被访问为STACK_POINTER + 14.

但是,当您引入可变大小的数组时,无法再在编译时计算这些偏移量; 其中一些将根据数组调用函数的大小而有所不同.这使编译器编写者的事情变得更加复杂,因为他们现在必须编写访问的代码STACK_POINTER + N- 并且由于N它自身变化,它也必须存储在某处.这通常意味着进行两次访问 - 一次STACK_POINTER + <constant>加载N,另一次加载或存储感兴趣的实际局部变量.


1.事实上,"在功能项中的堆栈指针的值"是这样一个有用的值,以具有围绕,它具有它自己的名字- 帧指针 -和多个CPU提供专用于存储帧中的单独的寄存器指针.实际上,通常是帧指针,从中计算局部变量的位置,而不是堆栈指针本身.

  • +1,这是迄今为止最好的答案. (2认同)

Joh*_*ski 7

支持并不是一件非常复杂的事情,所以C89不允许这样做的原因并不是因为当时不可能.

然而,有两个重要原因导致它不在C89中:

  1. 如果在编译时不知道数组大小,则运行时代码的效率会降低.
  2. 支持这一点使编译器编写者的生活更加艰难.

从历史上看,C编译器应该(相对)易于编写非常重要.此外,应该可以使编译器简单和小到足以在适度的计算机系统上运行(按80s标准).C的另一个重要特性是生成的代码应始终非常高效,没有任何意外,

我认为不幸的是,这些价值不再适用于C99.

  • 好吧,它不在C89的主要原因是因为C89是在*"标准化现有实践,不发明"*的前提下编写的,而且它不在现有的C实现中. (2认同)

Den*_*ssy 6

编译器必须生成代码以在堆栈上为帧创建空间以保存数组和其他本地局部变量.为此,它需要数组的大小.

  • 很多C的设计基于简单(对于编译器编写者)实现的内容.这就是为什么它是最低级别的"高级"语言之一.从C的早期版本中不需要遗漏某些东西,这只是非平凡的. (7认同)