为什么C99标准之前的C语言中for循环不支持初始化声明?

6

为什么原始的C语言不支持在for循环初始化中进行初始声明?

显然,最初的创建者以及之后的C99标准化并没有指定这样做。但我似乎找不到任何关于为什么做出这种设计选择的理由。

我能找到的最接近答案的东西是这个答案,它解释了为什么禁止混合声明和代码,以便编译器可以进行单通道处理,当时这一点非常重要。乍一看,在for循环语句中进行声明会有与声明与代码混合相似的问题。

但是,在C99之前的C语言确实支持在块开头进行声明:

{
    unsigned int i;
    for(i = 0; i < WHATEVER; i += 1)
    {
        /* ... */
    }
}

我个人认为编译器对此的逻辑与对此的逻辑在本质上并没有太大区别:

for(unsigned int i = 0; i < WHATEVER; i += 1)
{
    /* ... */
}

似乎如果编译器可以进行前面的单遍扫描,它也可以进行后者。这可能需要一个for语句总是创建一个作用域块(即使后面只跟着一个语句而不是一个{ ... }语句块),但我想不出这样的语义如何会破坏任何其他的C99之前的C代码(无论是for语句后面跟着一个语句块,在这种情况下,它已经是“有范围的”,或者它后面跟着一个单独的语句,在这种情况下,一个新的声明在那个单独的语句中也不会被允许)。
那么,为什么这种语法“特性”最初被省略了呢?我是否错误地认为支持它而不违反当时的性能目标是微不足道的?当时已知的语言解析器/编译器技术是否使它看起来更难?它是否只是因为极简主义设计/心态而被省略了,因为从功能上讲,做同样的事情是可能的(在for循环周围加上块)?还是有明确的语言设计理由反对它(例如,Go最初排除异常,因为设计者认为这样做会使语言更好)?

我所查找的地方

  • 我已经尝试在这里和通过一般的网络搜索中找到答案,但没有成功:我想到的所有搜索词似乎都充斥着关于C for循环初始声明、“在C99模式下使用”错误消息等混淆的问题(除了搜索词“基本原理”,它引导我找到了有用的信息,但没有具体回答这个问题)。
  • 我查阅了Dennis Ritchie本人关于开发语言的文章这里,但没有发现任何东西。
  • 我查阅了我的《C程序设计语言》(第2版)副本,首先阅读了实际的for循环解释部分,然后检查了索引以查找其他提到“for”/“for loop”的地方。我阅读了其他几个可能提到它的地方,但什么也没找到。

1
在C99之前,C语言不允许你在块的任何位置定义变量。在for循环的控制部分定义的变量并不在块的开头,因此没有期望它们会被支持。C++引入了这两个概念(在块中任何位置定义变量以及在for循环的控制部分定义变量)- C99也加入了它们。 - Jonathan Leffler
唯一能回答这个问题的是作者,其他人只会猜测。这类问题被视为离题。 - cimmanon
@cimmanon 我不这么认为(对于“推测”的含义,它与“基于现有证据的合理推理”足够不同以便有用):当时盛行思想的历史证据(当时计算机科学家的引用),技术证据表明它会恶化编译器的性能,甚至是合理的逻辑推理也可以产生比仅仅推测更加确定/客观的答案。 - mtraceur
1
彼得的段落开头是“在1999年之前”,接下来的部分用略微不同的措辞阐述了我的观点;我认为没有必要编辑他的回答来涵盖我的观点。 - Jonathan Leffler
@mtraceur 请参考:http://meta.stackoverflow.com/questions/292416/is-a-question-that-can-only-be-answered-correctly-by-a-small-group-of-people-a-b - cimmanon
显示剩余2条评论
1个回答

12

我认为没有任何具体的决定来排除这些功能,也没有充分的理由来这样做。

虽然我们可能会有点浪漫地认为设计者(Kernighan、Ritchie等)考虑了所有可能性,并经过深入而有意义的考虑后才排除了某些特性。但现实是,在设计C语言的早期(与其他一些编程语言类似),采用了一种更加谦逊的哲学,类似于“从小开始,除非程序员无法完成某些任务,否则不要担心添加功能。”

for循环中的变量初始化只是为了方便程序员 - 缺少它们并不会阻止事情的完成。因此,即使有人恳求或游说采用这样的特性(实际上可能并没有),也可能被放在优先级列表的较低位置。

至于事物如何发展......

在1999年之前,C语言(代码从{ 开始,到结束的闭合})块的变量声明位于块的开头,而不是在其他语句内部。这是在标准之前(K&R)的C以及前面的语言(如B语言)中工作的方式。

在C ++中,for循环内的变量声明/初始化首先被引入。它在早期出现(例如,在ARM的第19节),并最终在1998年底批准的第一个C ++标准中引入。

在起草C ++标准的过程中,C标准委员会进行了一些讨论,其中包括将C ++的一些特性纳入C语言。这些讨论通常主要是“如果我们添加了这个,C语言中是否还有其他内容会受到影响?”。许多编译器供应商已经将几种这样的功能作为可选扩展添加到其C编译器中(或者它们的C编译器实际上是C ++编译器,并具有禁用与C不兼容的C ++特性的设置),因此添加这些功能的讨论相当简短。因此,这些可从C ++轻松添加到C中的特性出现在1999年的C标准中。 for循环中的变量声明/初始化是其中之一。

从这段历史中,没有任何证据表明在早期的C语言中排除此类功能的任何特定决策或理由 - 简而言之,可能根本没有想过。


3
K&R厌倦了编写汇编语言,想要一种简单的语言,可以直接转换为汇编语言,以便他们可以继续在UNIX上工作。在我早期的日子里(1983年),一个优秀的C程序员是那些知道编译器生成哪种汇编代码的人。 - Paul Ogilvie
我认为如果在某些难以通过谷歌轻松验证的真相主张(例如C设计委员会讨论“C中是否会有任何问题”,以及“除非防止人们做某事,否则不要添加功能”是当时普遍采用的设计理念)中实际链接了实际来源,这个答案将会更好。基于我所知道的,我完全相信这些内容,但这将强化最可能感觉最“不客观”的部分。无论如何,除非出现更好的东西,否则我将在另一天接受这个答案。 - mtraceur
2
寻找C99标准的原理。在介绍的第一页附近,它说:“总体目标是开发一个清晰、一致和明确的[C]标准,该标准将编码C的常见、现有定义,并促进用户程序的可移植性……最初的X3J11章程明确要求编码常见的现有实践,而C89委员会坚持先例,只要那是清晰和明确的。大多数语言……与Brian Kernighan和Dennis Ritchie的《C程序设计语言》附录A中定义的完全相同……” - Jonathan Leffler
@JonathanLeffler:我希望编译器的作者们能够认识到,未定义行为是作为一种邀请,让编译器的作者们在考虑先例和程序员的需求(这通常会因目标平台和应用领域而异)时使用判断力,而不是将判断力抛之脑后。如果某些实现方式已经一致地以某种方式处理了某个操作,即使标准并不要求,也似乎难以想象标准的作者们有意这样做... - supercat
编译器的作者们会将缺乏强制性规定解释为可以忽略这种先例的判断。 - supercat

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