【问题标题】:Need help understanding this for loop code in C需要帮助理解 C 中的循环代码
【发布时间】:2012-06-20 15:41:19
【问题描述】:

考虑下面的 C 代码:

void main()
{
    int a=0;

    for(printf("\nA"); a; printf("\nB"));

    printf("\nC");
    printf("\nD");     
}

当我使用 Turb C++ 3.0 版和 gcc-4.3.4 编译它时,在这两种情况下我都会得到以下输出:

A
C
D

但是,如果我编译以下代码:

void main()
{
    for(printf("\nA"); 0; printf("\nB"));

    printf("\nC");
    printf("\nD");
}

gcc-4.3.4 的输出与前一种情况相同,但 turbo c++ 3.0 产生以下输出:

A
B
C
D

首先,我不知道这里发生了什么!另外,为什么 gcc 编译器的输出对于两个代码是相同的,但是在 turboc++ 3.0 编译器的情况下,输出是不同的?有人可以解释一下吗?

编辑:

其实有人在某IT公司的面试中被问到这个问题,当他没有给出答案时,面试官给出了这个解释。但我觉得这很愚蠢。您如何要求某人使用“错误”,就好像它是该语言提供的“设施”一样?为了将其称为“设施”和“技术”,无论我们在第二个表达式中将 0 作为文字传递还是作为值为 0 的变量传递,结果都应该是相同的。

我认为面试官很愚蠢地提出这样的问题并且表明他的无能是错误的吗?

【问题讨论】:

  • 这是编译器中一个有趣的区别,但是为什么你首先要使用那种for循环呢?
  • @ThomasPadron-McCarthy 实际上,语法很好。它似乎没有任何用处,但它完全有效,就像“我的感冒是脚趾”这句话在句法上完全有效,但却是一个无意义的短语。第一部分,printf("\nA"),将在循环开始时执行;哨兵表达式导致循环在计算结果为 false/0 时停止执行,它是第一个(== 0)的 a 和第二个的文字 0; [续]
  • @Naveen:很明显,它是在其中找到奇怪的错误。
  • [cont.] 最后一部分,printf("\nB"),通常在循环的每次迭代结束时执行,根本不应该执行,因为哨兵评估为 0 开始,并且在 3/4 的情况下是这种情况。
  • 这是一个编译器错误。关于 10 多年前 Turbo C++ 3.0 的这种奇怪行为,人们可以在 Internet 上找到更多类似的问题。甚至for (;0;) statement; 也会让statement 执行。请注意,TC++ 3.0 来自一个大多数 DOS 编译器不符合标准的时代,甚至实施了不兼容的扩展只是为了锁定他们的用户(例如 TC++ 与 MSC)。

标签: c


【解决方案1】:

第二个例子的 TCC 输出错误。

来自 C99 标准:

声明

for ( clause-1 ; expression-2 ; 表达式-3 ) 语句

表现如下:表达式 expression-2 是控制 在每次执行循环体之前计算的表达式。 表达式 expression-3 在之后被评估为 void 表达式 循环体的每次执行。 [...]

显然,这里没有迭代,所以永远不应该执行 expression-3

同样,在 C90 标准中(或至少在 draft that I found 中),它说:

除了循环体中 continue 语句的行为外, 声明

     for (  expression-1 ;  expression-2 ;  expression-3 )  statement

以及语句的顺序

      expression-1 ;
     while ( expression-2) {
               statement
              expression-3 ;
     }

是等价的。

【讨论】:

  • 显然 Turbo C++ 3.0 比 C99 早了 8 年。这可能是一些奇怪的优化技术。
  • 我同意。它也可能是编译器错误,很可能是。资源受限的 x86 架构的编译器生成不必要的函数调用是不正常的。
【解决方案2】:

gcc 输出是正确的。

第一种情况下的 Turbo C++ 3.0 输出是正确的。

第二种情况下的TurboC++ 3.0输出是错误的。

您似乎在 Turbo C++ 3.0 编译器中发现了导致错误代码生成的边缘情况。

C 或 C++ 中的 for-stmt 具有一般语法

for(初始化;测试;重新初始化)stmt

初始化在循环开始之前执行一次。测试在循环的顶部执行。如果测试为真,则执行 stmt,然​​后重新初始化,循环重复。

在您的情况下, printf("\nA") 是初始化,a(或 0)是测试, printf("\nB") 是重新初始化,并且 stmt 为空。

您应该已经看到 A(而且您确实看到了)。测试应该在第一次通过时失败,这意味着你不应该看到 stmt(但你不知道),你也不应该看到 B。这就是 Turbo C++ 3.0 在第二次测试中搞砸的地方。

【讨论】:

    【解决方案3】:

    for 的语义是第一个表达式被计算(初始化器)然后第二个表达式被计算(终止符)然后如果终止符被计算为非零,则执行 for 的主体,然后第三个表达式 ( progress) 被评估并返回评估终止符。

    由于您没有正文,因此该部分等于没有评估表达式。基于此,循环应按如下方式执行:

    printf("\nA");
    a; // yields 0 -> terminate loop
    

    确实是这样。

    在您的第二个示例中,应该会发生同样的情况(对于 gcc 也是如此),因为 0 的计算结果为 0。

    有可能 turbo C++ -- 看到 0 常量 -- 尝试执行某种循环展开优化(但未能正确执行)

    【讨论】:

    • 我几乎认为循环展开的故事是合理的,直到我意识到它不是i < 0,而只是0,这不是循环展开优化的可能目标。
    【解决方案4】:

    Turbo C++ 3.0 于 1990 年代发布,随后很快发布了 3.1。

    我猜你古老的编译器有很多错误,这些错误更新很快。此外,它可能没有有这样的错误,但可能发出了优化的程序集,在新的管道内衬架构下失败了。

    无论如何,保证 Turbo C++ 3.0 在您当前的平台上不支持。当谈到由于平台是在将近 20 年后创建的而不受支持的编译器时,您不能因为发出错误的程序而真正责怪它。

    【讨论】:

    • AFAIK,对 CPU 流水线等内容的更改对程序员应该是透明的。
    【解决方案5】:

    What is the full "for" loop syntax in C (and others in case they are compatible)?

    这个问题引用了标准的适用部分。除非循环至少执行一次,否则不应评估第三个表达式。所以,我想说的是,在第二种情况下,旧编译器打印'B'是错误的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-07-29
      • 2010-11-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多