【问题标题】:When does the increment expression execute [closed]增量表达式何时执行[关闭]
【发布时间】:2013-12-11 16:15:40
【问题描述】:

请告诉我为什么这个 C# for 循环执行后 sum 的值是 20 而不是 30:

for (int i = 2; i < 10; i += 2)
{
    sum += i;
}

在我看来,在第一次迭代结束时,我将是 2 和总和 2,在第二次结束时,我将是 4 和总和 6,然后是 6 和 12,然后是 8 和 20。然后,当循环开始到第 5 次迭代时,在 8 时 i 仍然小于 10,所以我将增加到 10 并且总和将是 20 + 10 = 30。然后下一次大约 i = 10,所以执行停止。但答案是 20。为什么循环不进入第 5 次迭代?请对我温柔一点。

非常感谢您的启发。

【问题讨论】:

  • 你需要 i
  • 了解调试器工作原理的好机会 :)
  • 这个问题似乎离题了,因为它要求我们执行提供的代码以获得答案。这就是编译器和调试器的用途。
  • @rene 看看ecma-international.org/publications/files/ECMA-ST/Ecma-334.pdf 的标准,关于“12.3.3.9 For statements”应该同样有效,尽管可能仍然是题外话
  • 您认为增量发生在检查之后。这种信念是不正确的。增量发生在检查之前。我鼓励您阅读规范以回答此类问题。

标签: c# for-loop increment


【解决方案1】:

循环执行4 times,当i2, 4, 6,8
总数为 20。

这里没有要解决的问题。当i 达到10 时,for 条件i &lt; 10 的评估为假,因此,10 永远不会添加到sum 变量中

要完成答案,for statement 的逻辑很简单:

  • 为控制退出表达式(i=2)的变量设置初始值
  • 评估退出表达式,如果为真则进入for body,如果为假则退出(i &lt; 10)
  • 执行for body { ... whatever... }
  • 为所需的步骤增加变量(i+=2)
  • 从退出表达式的计算重新开始

【讨论】:

    【解决方案2】:
    Loop 0 : i = 2, sum = 0 + 2 = 2
    Loop 1 : i = 2 + 2 = 4, sum = 2 + 4 = 6
    Loop 2 : i = 4 + 2 = 6, sum = 6 + 6 = 12
    Loop 3 : i = 6 + 2 = 8, sum = 12 + 8 = 20
    Loop 4 : i = 8 + 2 = 10, for loop stops.
    
    Result: sum = 20
    

    你写的for循环是这样的:

    int i = 2;
    while(true)
    {
      if (i < 10)
      {
          sum += i;
      }
      else break;
    
      i += 2;
    }
    

    所以i 被递增然后计算,如果它不满足条件,循环将被中断。

    【讨论】:

      【解决方案3】:

      for (a;b;c) d; 中,求值顺序是 a,b,d,(c,b,d),其中 (c,b,d) 重复(如果 b 曾经是 false,则循环在此处中断) .您似乎认为顺序是(b,c,d)。在调试器中单步执行代码将使这一点变得清晰。

      在您的情况下,这意味着在i == 8 之后,i 递增到10,然后测试i &lt; 10;因为它是false,所以在将10 添加到sum 之前循环中断。这是for 循环工作的更简单自然的方式,尤其是在循环数组等情况下:

      // actual; i will have values from 0 (inclusive) to array.Length (exclusive)
      for (int i = 0; i < array.Length; i++)
      // if it worked how you assumed, it'd be
      for (int i = 0; i < array.Length - 1; i++)
      // but the last time you go through the loop, i < array.Length - 1 isn't true
      

      【讨论】:

        【解决方案4】:

        正如其他人已经指出的那样,答案是循环不会进入第 5 次迭代,因为您示例中的 [stop-] 条件是“

        您可能会发现the MS docs about it 很有帮助。摘录如下:

        for(初始化器;条件;迭代器)

          body
        

        初始化部分设置初始条件。本节中的语句只运行一次,在您进入循环之前。该部分只能包含以下两个选项之一。

        • 局部循环变量的声明和初始化,如第一个示例所示 (int i = 1)。该变量是循环本地的,不能从循环外访问。

        • 零个或多个语句表达式 [...]

        条件部分包含一个布尔表达式,用于确定循环是应该退出还是应该再次运行。

        迭代器部分定义了循环体每次迭代后发生的情况。迭代器部分包含零个或多个 [...] 语句表达式,以逗号 [...]

        分隔

        作为旁注,可能值得一提的是,用于关系测试的运算符(如 '

        C# language specification - ECMA-334 在“12.3.3.9 For statements”中显示了非常清晰的定义,这也解释了 for 循环

        for (int i = 2; i < 10; i += 2)
        {
          sum += i;
        }
        

        可以翻译成while循环

        int sum = 0;
        int i = 2;
        while (i < 10)
        {
            sum += i;
            i += 2;
        }
        

        这使得指令的顺序更加明显。

        使用ildasm,除了一些NOP指令外,两个循环的输出是相同的。这是一个带注释的版本:

        .method private hidebysig static void  Testloop() cil managed
        {
          // Code size       27 (0x1b)
          .maxstack  2
          .locals init ([0] int32 sum,      <-- this is location 0 --> 1. int sum = 0;
                        [1] int32 i,        <-- this is location 1 --> 2. int i = 2;
                        [2] bool CS$4$0000) <-- this is location 2 --> 3. unnamed temporary result storage for the i < 10 comparison
          IL_0000:  nop --> no operation is the machine code equivalent of a space character and can be ignored
          IL_0001:  ldc.i4.0          --> 1. int sum = 0;
          IL_0002:  stloc.0           --> 1.
          IL_0003:  ldc.i4.2          --> 2. int i = 2;
          IL_0004:  stloc.1           --> 2.
          IL_0005:  br.s  IL_0011     --> branch to target IL_0011, which is a "goto" and jumps over the conditional check and iterator code which starts at IL_0007
          IL_0007:  nop --> no operation is the machine code equivalent of a space character and can be ignored
          IL_0008:  ldloc.0           --> 5. sum += i
          IL_0009:  ldloc.1           --> 5.
          IL_000a:  add               --> 5.
          IL_000b:  stloc.0           --> 5.
          IL_000c:  nop --> no operation is the machine code equivalent of a space character and can be ignored
          IL_000d:  ldloc.1           --> 6. i += 2
          IL_000e:  ldc.i4.2          --> 6.
          IL_000f:  add               --> 6.
          IL_0010:  stloc.1           --> 6.
          IL_0011:  ldloc.1           --> 3. i < 10
          IL_0012:  ldc.i4.s   10     --> 3.
          IL_0014:  clt               --> 3.
          IL_0016:  stloc.2           --> 4. continue until ( 3. ) is true (meaning, i >= 10) by jumping back to the start at IL_0007
          IL_0017:  ldloc.2           --> 4. 
          IL_0018:  brtrue.s  IL_0007 --> 4.
          IL_001a:  ret --> the closing bracket of the method Testloop()
        } // end of method Program::Testloop
        

        【讨论】:

          【解决方案5】:

          您似乎对for 的运作方式有误解。我将把循环变成执行的语句,这样你就会有一个更好的主意:

          int i = 2;
          if (i < 10) {
              sum += i; // sum = 2
          }
          i += 2; // i = 4
          if (i < 10) {
              sum += i; // sum = 6
          }
          i += 2; // i = 6
          if (i < 10) {
              sum += i; // sum = 12
          }
          i += 2; // i = 8
          if (i < 10) {
              sum += i; // sum = 20
          }
          i += 2; // i = 10
          if (i < 10) {
              // nothing, since 10 <= 10, but *not* 10 < 10
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2013-04-05
            • 2013-12-13
            • 2021-06-09
            • 2010-10-14
            • 2017-02-11
            • 1970-01-01
            • 2022-12-16
            • 1970-01-01
            相关资源
            最近更新 更多