【问题标题】:Rationale for pre-C99 C not having initial declarations in for loops?C99 之前的 C 在 for 循环中没有初始声明的理由?
【发布时间】:2016-05-08 18:00:31
【问题描述】:

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

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

我似乎找到的最接近答案的是this answer,它解释了如何禁止混合声明和代码,以便在重要时允许编译器单次返回。乍一看,for 循环语句中的声明与混合代码的声明存在相同/相似的问题是有道理的。

但是,C99 之前的 C 确实支持在块开始处声明:

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

我个人并不认为它的编译器逻辑与这个有什么本质不同:

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

在我看来,如果编译器可以执行前者的单遍,它也可以执行后者。它可能需要 for 语句 always 创建一个范围块(即使后面只有一个语句而不是 { ... } 语句块),但我想不出一种方法这种语义会破坏任何其他 C99 之前的 C 代码(for 语句后跟一个块,在这种情况下它已经“作用域”),或者它后跟一个语句,在这种情况下不允许新的声明无论如何,在那个单一的声明中)。

那么,为什么最初省略了这个语法“功能”?我认为在不违反当时的性能目标的情况下支持是微不足道的,我错了吗?当时已知的语言解析器/编译器技术是否使它看起来更难?它只是因为极简设计/心态而被省略了吗,因为在功能上可以做同样的事情(阻塞 for 循环)?或者是否有明确的语言设计理由反对它(例如,Go 最初是如何排除异常的,因为设计者认为这会产生更好的语言)?

我看过的地方

  • 我已经尝试在这里和通过一般的 web-search-fu 找到答案,但没有运气:我想到的所有搜索词似乎都充满了关于 C for 循环初始声明的困惑问题,“用于外部of C99 mode”错误消息等(除了搜索词“rationale”,它引导我找到有用的信息,但没有具体回答这个问题)。
  • 我在 Dennis Ritchie 本人的 this article 上搜索了有关开发该语言的内容,但没有发现任何内容。
  • 我搜索了我的 C 编程语言(第 2 版)副本,首先阅读了实际的 for 循环解释部分,然后检查索引中是否有其他提及“for”/“for 循环”的内容。我已经阅读了其他几个我认为可能会提到它的地方,但一无所获。

【问题讨论】:

  • 在 C99 之前,C 不允许您在块开头定义变量。在for 循环的控制部分中定义的变量不在块的开头,因此不期望它们会被支持。 C++ 引入了这两个概念(在块中的任意位置定义变量,以及在for loo 的控制部分定义的变量)——C99 也添加了它们。
  • 只有作者才能回答这个问题,其他人只是猜测。这些类型的问题被认为是题外话。
  • @JonathanLeffler 我赞成您的评论。您认为将其纳入当前给出的答案会更好吗?它类似于答案中的“除非程序员停止完成某事,否则不要添加功能”的想法,但捕捉到的看待问题的方式略有不同。
  • @cimmanon 我不这么认为(“推测”的含义与“基于现有证据的合理推理”有很大不同是有用的):流行思想的历史证据时间(当时计算机科学家的话)、技术证据表明它会如何恶化当时编译器的性能,甚至合理的逻辑推理也可以产生比推测更确定/客观的答案。
  • 彼得的段落以“1999 年之前”开头,下一段用稍微不同的措辞涵盖了我的观点;我认为没有必要编辑他的答案来涵盖我的观点。

标签: c c89 ansi-c


【解决方案1】:

我认为没有排除此类功能的具体决定,也没有理由这样做。

尽管看起来很浪漫,设计师(Kernighan、Ritchie 等)考虑了所有的可能性,并且只有在深入而有意义的考虑之后才排除功能,但现实是,设计 C 的早期(就像一个很少有其他编程语言)遵循更谦逊的理念,例如“从小处着手,不要为添加功能而烦恼,除非程序员被阻止做某事”。

for 循环中的变量初始化等功能为程序员提供了便利——它们的缺席并没有阻止事情的完成。因此,即使有人乞求或宣传此类功能(可能没有),它也可能按优先级排序。

至于事情是如何演变的.....

在 1999 年之前,变量声明位于 C 中的块的开头(代码以 { 开头,以 } 结尾),而不是在其他语句中。这是最初在准标准 (K&R) C 和之前的语言(如 B)中的工作方式(实际上是之前语言的缩减派生词)。

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

C 标准委员会在起草 C++ 标准的过程中(花了十年时间)就将 C++ 的某些特性应用到 C 中进行了一些讨论。讨论的主要内容是“在如果我们添加这个,C 会中断吗?”。许多编译器供应商已经在他们的 C 编译器中实现了几个这样的特性作为可选扩展(或者他们的 C 编译器实际上是 C++ 编译器,设置禁用与 C 不兼容的 C++ 特性),所以关于添加这些特性的讨论非常简短。因此,那些从 C++ 轻松添加到 C 中的特性出现在 1999 年的 C 标准中。 for 循环中的变量声明/初始化是这些功能之一。

从那段历史来看,没有证据表明有任何特定决定或理由将这些功能从早期 C 中排除 - 简而言之,可能根本就没有考虑过。

【讨论】:

  • K&R 只是厌倦了编写汇编程序,想要一种简单的语言,几乎可以直接翻译成汇编程序,这样他们就可以继续在 UNIX 上工作。在我早期(1983 年),优秀的 C 程序员是知道编译器生成什么汇编程序的人。
  • 我认为这个答案会更好与链接到某些难以琐碎的验证与谷歌真相声明的实际来源(例如 C 设计委员会讨论:“在C break”,并且“除非人们被阻止做某事,否则不要添加功能”是当时普遍采用的设计理念)。根据我所知道的,我完全认为那些是可信的,但它会强化那些最有可能让某些人觉得最“不客观”的部分。无论哪种方式,除非出现更好的情况,否则我会在另一天左右接受这个答案。
  • 找到 C99 的基本原理。在开头(引言的第一页)附近,它说:……总体目标是为 [C] 制定一个清晰、一致且明确的标准,该标准将 C 的通用、现有定义编入法典,并促进了 C 的可移植性。用户程序…… 最初的 X3J11 章程明确要求将现有的通用实践编成法典,C89 委员会坚持先例,只要这是明确和明确的。绝大多数语言……与 Brian Kernighan 和 Dennis Ritchie 的……C 编程语言的附录 A 中的定义完全相同……
  • @JonathanLeffler:我希望编译器作者能够认识到未定义行为的目的是邀请编译器作者对先例和程序员的需求进行判断(这通常会根据目标平台和应用领域),而不是作为将判断抛到窗外的邀请。如果某些类型的实现以某种方式一致地处理某个操作,即使标准不需要它,那么认为标准的作者打算......
  • ...编译器作者将缺乏授权解释为应忽略此类先例的判断。
猜你喜欢
  • 2015-07-21
  • 2011-08-18
  • 2014-09-10
  • 2013-01-19
  • 2013-10-28
  • 1970-01-01
  • 2015-06-02
  • 2017-10-28
  • 1970-01-01
相关资源
最近更新 更多