【问题标题】:Why does the C# compiler not even warn about endless recursion?为什么 C# 编译器甚至没有警告无限递归?
【发布时间】:2014-06-19 11:06:53
【问题描述】:

旧版应用在启动时处于无限循环中;我还不知道为什么/如何(代码混淆竞赛候选人),但关于被反复调用的方法(从其他几种方法调用),我想,“我想知道是否有一种方法调用这个是否也在调用另一个同样调用它的方法?”

我想:“不,编译器能够解决这个问题,并且不允许这样做,或者至少发出警告!”

所以我创建了一个简单的应用程序来证明是这样的:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        method1();
    }

    private void button2_Click(object sender, EventArgs e)
    {
        method2();
    }

    private void method1()
    {
        MessageBox.Show("method1 called, which will now call method2");
        method2();
    }

    private void method2()
    {
        MessageBox.Show("method2 called, which will now call method1");
        // Note to self: Write an article entitled, "Copy-and-Paste Considered Harmful"
        method1();
    }
}

...但是没有!它编译得很好。为什么编译器最多不会将此代码标记为有问题?如果任何一个按钮被捣碎,你就永远不会落地!

好的,有时您可能想要无限循环(起搏器代码等),但我仍然认为应该发出警告。

【问题讨论】:

  • 任何图形用户界面最基本的东西都是基于一个无限循环,一个事件循环。编译器确实没有理由对此发出警告,因为有很多有用的情况。而且,您的代码不会无限循环,它会被消息框打断。
  • 检测到无限循环通常是无法确定的。这是赖斯定理的结果。
  • 我什至不知道 Condoleeza 是一名程序员。还是说杰瑞?
  • 这不是无限循环,而是无限递归。这更糟糕,因为它会导致堆栈溢出。除非您正在编写恶意软件,否则永远不需要无限递归。
  • @B.ClayShannon 这也是一个无法解决的问题。您正在尝试解决停机问题,这是一个众所周知的无法解决的问题。

标签: c# .net recursion infinite-loop compiler-warnings


【解决方案1】:

我在 Infinite Loop Wiki 上找到了这个:http://en.wikipedia.org/wiki/Infinite_loop#Intentional_looping

在某些情况下,这是所需的行为。例如,基于卡带的游戏机上的游戏通常在其主循环中没有退出条件,因为没有可供程序退出的操作系统;循环一直运行,直到控制台关闭。

一旦完成卡片处理任务,老式穿孔卡片读取单元记录设备就会停止,因为不需要硬件继续操作,直到加载新的程序卡堆栈。

相比之下,现代交互式计算机要求计算机不断监视用户输入或设备活动,因此在某些基本层面上存在一个无限处理空闲循环,该循环必须持续到设备关闭或重置。例如,在 Apollo Guidance Computer 中,这个外部循环包含在 Exec 程序中,如果计算机完全没有其他工作要做,它将循环运行一个虚拟作业,该作业只会关闭“计算机活动”指示灯。

现代计算机在崩溃时通常也不会停止处理器或主板电路驱动时钟。相反,它们会退回到向操作员显示消息的错误条件,并进入一个无限循环,等待用户响应继续提示或重置设备。

希望这会有所帮助。

【讨论】:

    【解决方案2】:
    1. 正如您所说,有时人们想要无限循环。而且 .net 的 jit 编译器支持尾调用优化,因此您甚至可能不会像之前那样在无限递归中遇到堆栈溢出。

    2. 对于一般情况,在有限时间内预测程序是否将在某个时间点终止或陷入无限循环是不可能的。它被称为halting problem。编译器所能找到的只是一些很容易决定的特殊情况。

    【讨论】:

    • 不仅在有限的时间内不可能,而且是不可能的时期。
    【解决方案3】:

    因为它几乎不可能被发现!

    在您给出的示例中,很明显(对我们而言)代码将永远循环。但是编译器只是看到一个函数调用,它并不一定知道当时调用了那个函数,什么条件逻辑可以改变循环行为等等。

    例如,通过这个微小的改变,您不再处于无限循环中:

    private bool method1called = false;
    private void method1()
    {
        MessageBox.Show("method1 called, which will now call method2");
    
        if (!method1called)
           method2();
    
        method1called = true;
    }
    
    private void method2()
    {
        MessageBox.Show("method2 called, which will now call method1");
        method1();
    }
    

    如果没有实际运行程序,你怎么知道它没有循环?我可能会看到while (true) 的警告,但它有足够的有效用例,因此不给它发出警告也是有意义的。

    编译器只是解析代码并转换为 IL(无论如何对于 .NET)。您可以获得有限的信息,例如在执行此操作时未分配变量(尤其是因为它必须生成符号表),但像这样的高级检测通常留给代码分析工具。

    【讨论】:

      【解决方案4】:

      简单地说:编译器的工作不是质疑你的编码模式

      你可以很好地编写一个Main 方法,它只会抛出一个Exception。这是一种更容易检测的模式,也是一种更愚蠢的做法。但是编译器会很乐意让您的程序编译、运行、崩溃和烧录。

      话虽如此,从技术上讲,就编译器而言,无限循环/递归是完全合法的,因此没有理由抱怨它。

      实际上,在编译时很难确定循环在运行时永远不会被破坏。可能会引发异常,可能会发生用户交互,状态可能会在特定线程的某处发生变化,在您正在监视的端口上等等......毫无疑问,任何代码分析工具都有太多可能建立,即特定的递归代码段在运行时不可避免地会导致溢出。

      我认为防止这些情况的正确方法是通过单元测试组织。您在测试中覆盖的代码路径越多,您遇到这种情况的可能性就越小。

      【讨论】:

      • “质疑你的编码模式不是编译器的工作” 这不正是警告的用途吗?突出显示有效但很可能是错误的编码模式。
      • @svick 这取决于你所说的编码模式。对我来说,从未分配过的 var 或使用过时的成员都不是设计问题。
      【解决方案5】:

      这不是无限循环,而是无限递归。这更糟糕,因为它们可能导致堆栈溢出。除非您正在编写恶意软件,否则大多数语言都不需要无休止的递归。然而,无休止的循环通常是有意的。服务通常在无限循环中运行。

      为了检测这种情况,编译器必须按照方法调用来分析代码;但是,C# 编译器将此过程限制为当前方法中的立即代码。例如,在这里,可以跟踪未初始化或未使用的变量,并且可以检测到无法访问的代码。在编译速度和静态分析和优化的深度之间需要权衡取舍。

      也很难知道程序员的真正意图。

      假设您编写了一个完全合法的方法。突然因为你从另一个地方调用这个方法,你的编译器抱怨并告诉你你的方法不再合法。我已经可以在 SO 上看到大量帖子,例如:“我的方法昨天编译。今天它不再编译。但我没有更改它”。

      【讨论】:

      • 这更像是循环引用递归(我的意思是 OP 发布的代码)。
      • 这只是递归的一个特例。
      • “几乎不可能知道程序员的真实意图” 通常可以安全地假设最坏的情况,似乎。
      • 某些看起来很糟糕的情况可能是故意的,但在某些情况下仍然有意义。编译器不是人类意义上的智能,它只是一台机器。
      • 如果编译器开始抱怨代码错误,全世界的开发计算机都会因突然涌现的大量警告消息而疲惫不堪。 :)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-28
      • 2021-06-27
      • 2010-12-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多