32

在 C 编程语言中,我使用的所有语言修订版都在评估任何非声明性/赋值表达式之前强制执行了预先变量声明。C++ 似乎已经放弃了所有版本的这个要求。我也认识到更现代的 C 版本也放弃了这个要求,但我还没有使用任何这些标准。

我的问题是:有什么历史原因阻止 C 语言按需自由声明而不是预先声明?

显然,从工程的角度来看,我想到了许多原因,但对我来说,没有一个是特别合理的。

  1. 防止发生晦涩的编译器行为错误(例如无限解析循环、用于评估的大量内存膨胀或宏的一些奇怪的极端情况。)
  2. 防止不需要的编译器输出。这可能是任何东西,从混淆调试过程的符号输出和调试工具开发的简易性,到意外的堆栈存储顺序。
  3. 可读性。我也觉得这很难接受,因为 C 虽然与那个时代的其他语言相比是为了可读性而设计的,但几乎没有在其他任何地方强制执行这种类型的结构。(除非您将原型设计视为类似的强制执行,但如果我记得原型是在 '89 规范中添加的。)
  4. 实现复杂性和实际原因。这是我最倾向于相信的一个。作为工程师,我们必须做出某些考虑,以便在分配的时间范围内交付可行的产品。虽然我承认计算机科学和软件工程的专业领域都发生了巨大变化,但商业仍然是商业。归根结底,我确信贝尔想要一个可以在 Unix 编程环境中使用的成品来展示他们所取得的成就。

有没有人有任何好的资源来支持上述任何内容?我完全错过了什么吗?我们可以从黎明到黄昏进行推测,但我正在寻找好的硬参考。

4

3 回答 3

19

查看Dennis Ritchie 主页上的早期(第 6 版 Unix,1975) C 手册,在那个版本中,函数局部变量只能在函数的开头声明:

函数语句只是一个复合语句,它可能在开始时有声明。

函数声明:{声明列表选择 声明列表}

声明列表未定义(省略),但可以很容易地假设具有语法:

声明列表声明 声明列表选择

不允许其他复合语句包含变量(或实际上任何)声明。

这显然简化了实现;在早期的编译器源代码c02.c中,函数头函数blkhed()只需要对auto变量声明使用的堆栈空间求和,同时记录它们的堆栈偏移量,并发出代码以适当的量碰撞堆栈指针。在函数退出(return结束或结束)时,实现只需要恢复保存的堆栈指针。

K&R 觉得有必要声明“变量的声明(包括初始化)可能跟在引入任何复合语句的左大括号之后,而不仅仅是开始函数的声明”这一事实暗示了当时它是一个相对较新的功能. 它还表明组合声明初始化语法也是最近的一个特性,并且确实在 1975 年的手册中声明器不能有初始化器。

1975 年手册第 11.1 节特别指出:

C 不是块结构语言;这可以公平地被认为是一个缺陷。

块语句和初始化声明 (K&R) 解决了该缺陷,混合声明和代码 (C99) 是逻辑延续。

于 2013-01-14T20:11:47.820 回答
10

在 C89 中,变量定义必须位于块的开头。(有关块的定义,请参见 C 标准)据我所知,这是为了简化在汇编程序中处理变量的方式。例如,让我们看一个简单的函数:

void foo()
{
    int i = 5;
    printf("%i\n", i);
}

当 gcc 将此函数转换为汇编代码时,对 foo() 的调用将归结为一堆指令,包括为函数范围设置堆栈帧。这个堆栈帧包括在函数范围内定义的变量的空间,并且为了匹配高级语言 C 中的相同范围,它们需要在块的开头定义。

最后,它是关于易于实现和效率,因为一次声明一堆变量,它在块的开头,使编译器能够将它们批量推送到堆栈上,大约 89这也是一个性能考虑。

当然,这个答案非常简化,只是为了简要说明为什么会以这种方式完成。有关更多信息,您可能应该阅读早期 C89 标准的一些草案。

于 2013-01-14T19:43:06.857 回答
10

一个没有真正回答的简短答案:C 语言最初从其前身 B 语言继承了这种声明顺序限制。不幸的是,我不知道为什么用 B 语言这样做。

另请注意,在新生的 C 中(在“C 参考手册”中描述)使用非常量表达式初始化变量(甚至是局部变量)是非法的。

int a 5;
int b a; /* ERROR in nascent versions of C */

(附注:在 CRM 初始化语法中不包含该=字符)。在一般情况下,这有效地否定了代码内变量声明的主要好处:将有意义的运行时值指定为初始值设定项的能力。即使在更现代的 C89/90 中,这个限制仍然正式应用于聚合初始化器(尽管大多数编译器都忽略了它)

int a = 5, b = a;
struct { int x, y; } s = { a, b }; /* ERRROR even in C89/90 */ 

只有在 C99 中,才有可能使用运行时值进行各种本地初始化。这最终释放了代码内变量声明的全部功能,因此 C99 引入它们是完全合乎逻辑的。

于 2013-01-14T19:50:50.753 回答