如何发现未定义的行为

时间:2010-06-10 15:52:21

标签: c++ c undefined-behavior

有没有办法知道你的程序是否在C ++(甚至是C)中有未定义的行为,缺少记忆整个规范?

我问的原因是我注意到很多程序working in debug but not release由于未定义的行为而导致。如果有一个工具至少可以帮助发现UB,那就太好了,所以我们知道存在问题的可能性。

10 个答案:

答案 0 :(得分:15)

良好的编码标准。保护你自己。以下是一些想法:

  1. 代码必须以最高警告级别编译... 没有警告。 (换句话说,当设置为最高级别时,您的代码根本不能引发任何警告。)打开所有项目的警告标志错误。

    当你使用其他人的图书馆时,这确实意味着一些额外的工作,因为他们可能没有这样做。你也会发现有些警告没有意义......当你的团队决定将这些警告单独关闭时。

  2. 始终使用RAII

  3. 永远不要使用C风格的演员表!从不! - 我认为有一些罕见的情况,你必须打破这个,但你可能永远找不到它们。

  4. 如果您必须reinterpret_cast或强制转换为void,请使用包装器确保您始终使用相同类型进行转换。换句话说,将指针/对象包装在boost::any中并将指针投射到您需要的任何内容中,并在另一侧执行相同操作。为什么?因为您始终知道来自reinterpret_cast的类型,boost::any将强制执行您之后转换为正确的类型。这是最安全的。

  5. 始终 在声明点初始化您的变量(或在类中的构造函数初始值设定项中)。

  6. 还有更多,但这些是一些非常重要的开始。

    没人能记住这个标准。我们高级C ++程序员所做的工作是使用我们知道的安全结构并保护自己不受人类本性的影响...我们不使用不安全的结构,除非我们必须这样做,然后我们会特别注意确保危险都被包裹在一个很好的安全界面中,经过了地狱和背部测试。

    要记住哪一个在所有语言中具有普遍性的重要事项是:

      

    使您的构造易于正确使用且难以正确使用

答案 1 :(得分:5)

在所有情况下都无法检测到未定义的行为。例如,考虑x = x++ + 1;。如果你熟悉这门语言,你知道它是UB。现在,*p = (*p)++ + 1;显然也是UB,但是*q = (*p)++ + 1;呢?那是UB q == p,但不是它的定义(如果看起来很笨拙)。在给定的程序中,很可能证明pq在到达该行时永远不会相等,但通常无法完成。

要帮助发现UB,请使用您拥有的所有工具。好的编译器会警告至少更明显的情况,尽管你可能必须使用一些编译器选项来获得最佳覆盖。如果您有其他静态分析工具,请使用它们。

代码审查也非常适合发现此类问题。如果您有多个开发人员可以使用它们。

答案 2 :(得分:3)

PC-Lint等静态代码分析工具可以为此提供很多帮助

答案 3 :(得分:2)

嗯,this文章涵盖了大多数方面..

答案 4 :(得分:2)

我认为您可以使用coverity中的一个工具来发现导致未定义行为的错误。

我猜你可以使用theorem provers(我只知道Coq)来确保你的程序符合你的要求。

答案 5 :(得分:2)

clang在遇到未定义的行为时会尽力产生警告。

答案 6 :(得分:1)

我不知道有任何软件工具可以检测所有形式的UB。显然,使用编译器的警告以及可能的lint或其他静态代码检查器可以提供很多帮助。

另一件很有帮助的事情就是经验:你对语言进行编程的次数越多,你就会看到越多看起来令人怀疑的结构,并且能够在过程的早期就抓住它们。

答案 7 :(得分:0)

简单:不要做你不知道可以做的事情。

  • 如果您不确定或有腥味,请查看参考资料

答案 8 :(得分:0)

不幸的是,没有办法检测所有UB。你必须解决停机问题。

您可以做的最好的事情就是尽可能多地了解规则,在遇到疑问时查阅,并与其他程序员核实(通过结对编程,代码审查或只是SO问题)

使用尽可能多的警告进行编译,并在多个编译器下可以提供帮助。通过静态分析工具(如Valgrind)运行代码可以检测到许多问题。

但最终,没有任何工具可以检测到所有这些。

另一个问题是,许多程序实际上必须依赖于UB。有些API需要它,并假设“它适用于所有理智的编译器”。 OpenGL在一两个案例中做到了这一点。 Win32 API甚至不会在符合标准的编译器下编译。

所以,即使你有一个神奇的UB检测工具,它仍然会被不受你控制的情况绊倒。

答案 9 :(得分:-3)

一个好的编译器,比如英特尔C ++编译器,应该能够发现99%的未定义行为。您需要调查要使用的标志和开关。与以往一样,请阅读手册。