如何发现未定义的行为

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

我问的原因是我注意到很多程序在调试中工作但没有发布是由于未定义的行为。 如果有一个工具至少可以帮助发现UB,那就太好了,所以我们知道存在问题的可能性。

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

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

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

  2. 始终使用RAII

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

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

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

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

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

要记住哪一个在所有语言中都是通用的重要事项是:

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

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

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

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

诸如PC-Lint之类的静态代码分析工具可以在这里提供很多帮助

那么, 这篇文章涵盖了大多数方面..

我认为你可以使用一个覆盖率工具来发现导致未定义行为的错误。

我想你可以使用定理certificate器 (我只知道Coq)来确保你的程序能做你想要的。

当遇到未定义的行为时, clang努力产生警告。

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

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

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

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

不幸的是,没有办法检测所有UB。 你必须解决停止问题才能做到这一点。

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

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

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

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

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

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