【问题标题】:What's the logic behind this C code?这段 C 代码背后的逻辑是什么?
【发布时间】:2010-09-28 13:32:20
【问题描述】:
for(int i=0 ; i++ ; printf("%d",i));
printf("%d",i);

输出为 1。 如果我们让 i = 1 则有一个荒谬的输出,如果 i = -1 则输出为 01。 For 循环是如何工作的?

【问题讨论】:

    标签: c logic


    【解决方案1】:

    for循环的测试部分是i++

    因为++(或增量)是post-fix(写在变量之后,而不是之前),增量发生在在评估语句之后并经过测试。测试在 0 上进行,其评估结果为 FALSE,因此“循环”存在而从未运行过。

    接下来,后缀++ 生效,将i 从0 更改为1。

    正如@paxdiablo 指出的那样,一旦循环退出,i 就超出了范围。 无论最终 printf 打印的是什么,它都是一个不同的 i,声明并给出了一个未在您的代码中显示的值。


    在其他情况下,如果你从 1 开始 i,那么测试总是正确的,并且每个数字都被打印出来(直到 i 溢出,并返回到 0)。


    最后,如果i 从 -1 开始,则测试最初通过(-1 为 TRUE),发生后修复增量,将 -1 变为 0,并打印 0

    循环再次运行,这次 0 测试失败,循环结束,后修复增量发生,另一个i(未在您的代码中显示)在循环结束后打印出来。

    【讨论】:

    • 注意,i 只存在于for 的范围内。无论i 在循环之后被打印出来是一个不同的 (在for 循环运行时隐藏)。循环对该变量的影响为零。
    • 我似乎记得一些错误的编译器仍然会让你访问相同的i,这会使这段代码成为一个有用的测试用例,尽管我当然希望人们不必处理这样的问题一个编译器!
    • 请注意,溢出并返回 0 并不能保证发生,尽管它会在我过去二十年来编程的每个系统上发生。
    • @Arafangion:是的,请参阅stackoverflow.com/questions/984878/… 接受的答案
    • @David Thornley:我同意。规范并不能保证它会发生,但我认为这是一个安全的选择。
    【解决方案2】:

    for 循环甚至不会运行 - i 在条件下测试时为 0,before 被递增。此外,除非i 也在循环之前的某处定义,否则代码将无法编译,因为printf 中的i 循环之后是未定义的。

    您到底是从哪里获取此代码的?这是编写for 循环的一种非常怪诞的方式,我永远不会在混淆编码竞赛之外使用它。

    【讨论】:

    • 如果是作业或面试问题,我不会感到惊讶。他们喜欢问这样棘手的问题。
    • @cHao:我只希望他们在询问棘手的问题时能坚持合法、明确的行为。
    • i 仍将递增。 (也许这只是不好的措辞......循环确实没有运行 - 并且在 'i' 递增之前确实检查了测试条件,但它仍然根据该语法递增。)也就是说,编译器应该能够在这种情况下进行优化。
    • @Arafangion:我没有建议 i 不会增加,但它也会立即超出范围,所以就像森林中那棵众所周知的树听到倒下;-)
    【解决方案3】:

    它不应该编译,除非之前声明了 i

    代码在 C90 中是非法的,因为除了块的开头之外不允许声明。在 C99 中,ifor 循环中的声明是合法的,但它的范围是循环,并且不会超出它。这在标准 C++ 中也是如此,但在准标准 C++ 中,范围确实扩展到封闭块的末尾。当然,有可能编译器不符合任何已发布的标准,在这种情况下,我们不能说绝对会发生什么。

    for 循环有三个子句。第一个在最开始运行,没有其他地方,并且(在 C99 和 C++ 中)可能是一个声明。它建立i的起始值。

    第二个子句在循环体之前运行。如果计算结果为 false,for 语句将立即结束。由于iint,唯一的假值为0。在这种情况下,i++ 返回i 的当前值并将其递增以供以后使用。因此,如果i 为 0,则 for 循环什么也不做。如果i 为1,则i 将递增,直到i 为0 或发生其他情况。这实际上是未定义的行为,因为i 是有符号整数类型,但通常会发生的情况是它会递增到可能的最正值int,然后变成可能的最负值int,然后递增直到它是 0 并且循环停止。如果 i 从 -1 开始,那么在第一次运行时它不是 0,但在第二次运行时会是。

    这里执行打印的第三个子句在循环体之后运行。它通常用于递增循环正在执行的任何操作,它可能在整数范围内运行,或者在链表中的指针链或其他内容中运行。除了被执行之外没有任何作用。

    for 循环的主体为空,如右括号后紧接的; 所示。这意味着,在执行第一个子句后,将交替执行第二个和第三个子句,只要第二个子句的计算结果为 0,就会停止。

    这是一个非常奇怪的for 语句,因为没有循环体,并且在第二个子句中完成了增量,但它是合法的(除非存在算术溢出,如果i 开始为正,则会发生这种情况)。紧随其后的未定义 i 不是。

    【讨论】:

      【解决方案4】:

      for(int i=0 ; i++ ; printf("%d",i)); 中,测试条件为假(因为i++ 返回0),循环不会运行。

      注意循环后面的分号。 :)

      【讨论】:

        【解决方案5】:

        这看起来像是乱码。我不敢相信这是故意的。

        for() 语句的第二部分指定循环终止的条件。如果条件为真,则循环结束,否则循环。

        在这种情况下,语句的第二部分是i++。这通常在 if() 语句的第三部分。通过放入第二部分,您将强制循环仅在i++ 评估为假(即零)时退出。因此,如果i 开始小于零,您将获得迭代直到它达到零,否则您将获得一个无限循环。

        正如我所说,++ 通常在语句的第三部分,所以从表面上看,好像有人删除了 if() 语句的中间部分,出现编译错误,并且试图通过在最后卡住 printf() 来修复它。凌乱。

        【讨论】:

          【解决方案6】:

          此循环在 i 为 0 时结束。因此,如果您从 0 以上开始,则 i 将在 2^31 或 2^63 次迭代后为 0...。

          【讨论】:

          • 您的意思是 (2^32 - 1) 或 (2^64 - 1),因为它也会遍历所有负数...
          【解决方案7】:

          ++ 用作后增量。因此,当您测试 i 的值时,它将是 0 并且您根本不会进入循环。在循环之外打印i 的值,现在是1

          【讨论】:

          • 感谢 evry1 的帮助。我明白了。这个问题出现在 GATE 的一篇论文中。
          • @Brite Roy:什么是 GATE 论文,为什么它坚持调用未定义的行为?
          • Gate 是在印度举行的考试。
          • @Adriano...好的,我会处理的。
          • @Brite:他们在官方国家考试中问这样的东西?搞砸了。
          【解决方案8】:
          for(int i=0 ; i++ ; printf("%d",i)); printf("%d",i);
          

          等价于

          int i = 0; 
          
          while (i++)
          {
             printf("%d",i);
          }
          
          printf("%d",i);
          

          当 i = 0 最初时,i++ 为 0(因此为假),因此循环永远不会被执行。但是我被设置为 1,所以最后一个 printf 打印 1。

          最初当 i = -1 时,i++ 为 -1(非零,所以为真),因此循环体执行。在 i++ 之后,i 为 0,所以第一个 printf 打印 0。我们现在已经将问题简化为前一种情况,所以剩下的输出是 1。所以整体输出是“01”,0 和 1彼此相邻,因为您没有在 printf 格式字符串中添加任何空格。

          当 i = 1 最初时,循环执行 i = 1,然后 i = 2,然后 i = 3,...,然后 i = 2147483647(或任何INT_MAX),然后环绕到i = -2147483648,然后 i = -2147483647,然后 i = -2147483646,最终在打印了数十亿个数字后返回到 0。

          【讨论】:

          • grrt 解释 dan04.Thnku 这么多。
          • 抱歉吹毛求疵,但您的等效代码不是。无论i 在第二个printf 中打印什么,都不是在第一个中打印的(其范围仅限于for 本身)。除此之外,还不错。更好的是像`{ int i = 0; while (i++) { printf("%d",i); } } printf("%d",i);
          • @Brite Roy:根据标准,最后一段是不正确的,尽管它适用于大多数现代系统。有符号整数溢出在 C 和 C++ 中未定义。此外,如果这是 C 或标准 C++,最后一个 printf 中的 i 不是循环中的 i
          • 这也是一个不正确的解释,尽管一些编译器会允许它这样工作。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-10-17
          • 2021-12-05
          • 2020-05-22
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多