【问题标题】:Difference between for(;;) and while (true) in C#?C# 中 for(;;) 和 while (true) 的区别?
【发布时间】:2011-03-21 19:42:21
【问题描述】:

从句法上讲,我看到它们无限循环,直到到达 break 语句,但是它们被编译成相同的东西吗?是因为它没有条件检查而稍微快一点吗?除了代码可读性之外,还有区别吗?

【问题讨论】:

  • 这是个好问题。在我尝试过的示例代码中,我发现编译器将代码优化为 do while 循环,这很有趣。
  • for(;;) 是 A) 由以前的 C 编码人员编写,B) 缩短了四个字符 :)

标签: c#


【解决方案1】:

我没有检查输出代码,但应该没有任何区别。任何体面的编译器都会做足够简单的循环优化来查看条件是一个常量表达式,因此不需要检查每次迭代。

如果一个比另一个快,C# 编译器编写者需要向他们解释一些东西......

【讨论】:

    【解决方案2】:

    在现代编译器中,绝对没有。

    然而,从历史上看,for(;;) 是作为单次跳转实现的,而while(true) 也可以检查是否为真。

    我更喜欢while(true),因为它让我更清楚我在做什么。

    【讨论】:

    • 绝对+1。 while(true)for(;;) 清晰得多。无论如何,我还在等什么?
    • 我非常同意你应该选择你认为更清晰的那个,尽管我可能不同意哪个是......
    • 为什么不loop: ... goto loop
    • 天啊!不是goto!如果我们使用它,世界将会终结!
    • 如果我们使用goto,我认为世界不会终结,但它可能会突然跳转到宇宙中的新位置。
    【解决方案3】:

    鉴于此输入:

    private static void ForLoop()
    {
        int n = 0;
        for (; ; )
        {
            Console.WriteLine(n++);
        }
    }
    
    private static void WhileLoop()
    {
        int n = 0;
        while (true)
        {
            Console.WriteLine(n++);
        }
    }
    

    ...你得到这个输出:

    .method private hidebysig static void  ForLoop() cil managed
    {
      // Code size       14 (0xe)
      .maxstack  3
      .locals init ([0] int32 n)
      IL_0000:  ldc.i4.0
      IL_0001:  stloc.0
      IL_0002:  ldloc.0
      IL_0003:  dup
      IL_0004:  ldc.i4.1
      IL_0005:  add
      IL_0006:  stloc.0
      IL_0007:  call       void [mscorlib]System.Console::WriteLine(int32)
      IL_000c:  br.s       IL_0002
    } // end of method Program::ForLoop
    
    
    .method private hidebysig static void  WhileLoop() cil managed
    {
      // Code size       14 (0xe)
      .maxstack  3
      .locals init ([0] int32 n)
      IL_0000:  ldc.i4.0
      IL_0001:  stloc.0
      IL_0002:  ldloc.0
      IL_0003:  dup
      IL_0004:  ldc.i4.1
      IL_0005:  add
      IL_0006:  stloc.0
      IL_0007:  call       void [mscorlib]System.Console::WriteLine(int32)
      IL_000c:  br.s       IL_0002
    } // end of method Program::WhileLoop
    

    我会说非常相似(相同,甚至)。

    【讨论】:

    • 除非我遗漏了什么,否则它们是相同的,在 cmets 和标识符名称之外。
    • @T.E.D.是的,它们是相同的。
    • 嗯,第 5 行有所不同...哦,等等,不,那是我显示器上的一个点;)
    • 我们真的在讨论无限循环的相对速度吗?
    • @Steven:我当然希望不会。我们满足了我们的好奇心。至少我是。
    【解决方案4】:

    它们编译成相同的东西。您可以编写一个实现这两种方法的测试应用程序,然后使用 ILDASM 确认它们的 IL 相同。

    【讨论】:

    • 或者你可以看看 Fredrik 的回答,省去麻烦。 :)
    【解决方案5】:

    调试程序集遵循 while(true)。使用反射器,您可以看到结果。

        static void Main(string[] args)
        {
            ExecuteWhile();
    
            ExecuteFor();
        }
    
        private static void ExecuteFor()
        {
            for (; ; )
            {
                Console.WriteLine("for");
                string val = Console.ReadLine();
                if (string.IsNullOrEmpty(val))
                {
                    Console.WriteLine("Exit for.");
                    break;
                }
            }
        }
    
        private static void ExecuteWhile()
        {
            while (true)
            {
                Console.WriteLine("while");
                string val = Console.ReadLine();
                if (string.IsNullOrEmpty(val))
                {
                    Console.WriteLine("Exit while.");
                    break;
                }
            }
        }
    

    检查 Reflector 中的ExecuteFor 方法。

    private static void ExecuteFor()
    {
        while (true)
        {
            Console.WriteLine("for");
            if (string.IsNullOrEmpty(Console.ReadLine()))
            {
                Console.WriteLine("Exit for.");
                return;
            }
        }
    }
    

    同一代码的优化版本会为ExecuteFor 产生不同的结果

    private static void ExecuteFor()
    {
        do
        {
            Console.WriteLine("for");
        }
        while (!string.IsNullOrEmpty(Console.ReadLine()));
        Console.WriteLine("Exit for.");
    }
    

    这里的冗长是优化的ExecuteWhile...

    private static void ExecuteWhile()
    {
        do
        {
            Console.WriteLine("while");
        }
        while (!string.IsNullOrEmpty(Console.ReadLine()));
        Console.WriteLine("Exit while.");
    }
    

    【讨论】:

    • 真的编译成while(true),还是Reflector反编译成那样? :p
    • 如果您查看从相同程序集创建的 IL,它会以这种方式读取。
    【解决方案6】:

    正如其他人所提到的,对于任何现代编译器,它应该完全没有区别。

    我在我的电脑上做了一个简短的测试,使用一个或另一个来计时几个操作,它们总是花费几乎相同的时间。当然,由于其他进程正在运行,这些测试并不是 100% 准确的,但如果存在速度差异(不应该存在),那就是微观差异。

    【讨论】:

      【解决方案7】:

      如果可以的话,我建议您看一个稍微不同的问题。如果您经常使用其中任何一种来关心,那么您的代码结构可能很糟糕。虽然像嵌入式系统这样的东西确实可以永远运行,但大多数普通代码中的循环却不是。编写一个声称永远运行的循环通常意味着您已经将循环的退出条件隐藏在内部某处,而将其他一些控制流(例如,if (whatever) break;)作为真实 em> 退出循环。

      可以而且通常应该避免这种情况。虽然在某些情况下break 语句是有意义的,但它们通常应该用于处理不寻常的情况,而不是编写一个说一件事但做另一件事的循环(即,说“永远运行”,但确实“运行直到条件满足遇见”)。

      【讨论】:

      • 我也没有。当我第一次学习在 PL/I 中编程时(我不知道它是否有'break'语句),模式是“读取文件行;虽然它不是结束标记,但执行循环,结束阅读下一行”。这似乎违反了“不要重复自己”。我建议在循环开始时读取该行一次,如果它是结束标记则退出,这是一种更好的风格。
      • @supercat:我同意,但这并不能证明任何事情的合理性。你应该做while (file.getline()),而不是for (;;) if (!file.getline()) break;
      • @supercat:是的,将测试放在循环的中间并在失败时退出它通常是最干净的。
      • @Jerry:这是一个看似合理的例子,但并不是唯一可行的方法。哨兵可以是一个特定的值,例如“0”。
      • @Jerry Coffin:while() 循环条件超出了我的阈值,即表达式在被认为荒谬之前可以产生多少副作用。为什么要打扰循环体呢?为什么不是“while ((((x=next_value()) != sentinel) && (process(x),1));”?
      猜你喜欢
      • 2014-05-19
      • 1970-01-01
      • 2019-06-24
      • 1970-01-01
      • 1970-01-01
      • 2011-01-16
      • 2023-03-14
      • 2019-03-28
      • 2014-10-03
      相关资源
      最近更新 更多