处理可能包含未定义行为的项目

Posted

技术标签:

【中文标题】处理可能包含未定义行为的项目【英文标题】:Deal with project which may contain undefined behaviour 【发布时间】:2016-01-29 06:37:11 【问题描述】:

我想建议在这种情况下如何进行。 想象一下,我有一个运行良好的大型 C++ 项目。

我怀疑这段代码中可能有一些 UB(因为在同一作者编写的不同项目中我发现了 UB)。

现在,假设我需要为这个项目添加新功能。 我害怕是因为:

如果我用新的编译器重新编译,如果代码中已经是 UB,这会增加发生 UB 的风险。 (例如,新编译器可能无法与旧编译器兼容的 UB 兼容)。

在这个大型项目中通过肉眼检查消除所有 UB 是否现实(在我开始添加新功能之前)?

如果不是,那么我至少应该使用相同版本的编译器进行编译,对吧? (如果有 UB,以减少出现问题的机会)。

项目是在 Visual Studio 中完成的,所以我不知道是否有目标文件,在这种情况下,我可以保持目标文件不变,只修改需要添加内容的文件中的部分 - 从而再次最大限度地降低 UB 的风险.

在这种情况下采取什么行动?我认为这可能是很常见的情况。


我喜欢建议在添加新代码之前使用新编译器测试项目,但即便如此 - 我们知道测试可能不会显示 UB,不是吗?

【问题讨论】:

不要害怕遇到错误。最好找到它们,而不是在您不知情的情况下将它们包含在代码中。 几乎任何大型项目都必然包含一些 UB,当切换到新的编译器时,通常会出现一些潜在的错误。我不认为你可以做任何魔法来避免它。只需编写好的测试并聘请好的测试人员。 @user200312 正如其他答案所说,最新的编译器是一个非常好的建议。此外,最新的 VS 在分析器中有一些非常创新的功能。 用新编译器编译并运行一些测试在对代码进行任何更改之前。如果它有效,您可以继续,确保新编译器不会造成灾难;如果它出了问题,这不是你的错,并且你有证据证明旧代码有错误。 @user200312 您知道在现实世界中,如果您没有 100% 无错误的程序也没关系,对吧?那不是因为错误是可以的,而是因为你永远不可能有一个 100% 没有错误的程序。您所能做的就是尽最大努力检测它们。这就是测试和分析仪的用途。当您检测到错误时,您会修复它。没有其他方法可以解决它。有时您会在产品发货后检测到错误。这就是更新和错误修复的目的。 【参考方案1】:

按顺序,我会:

    使用-Wall(Windows 用户为/W4)编译并修复错误。 如果还没有测试,请编写测试。 使用valgrind 等工具检测并修复问题。 研究正在使用的同步原语,并尽可能使用现代范例。 记录代码并遵守样式指南。

我不会试图通过保留目标文件来避免问题。这是一个噩梦般的维护问题。

【讨论】:

OP使用Visual Studio,相当于gcc的-Wall是/W4 你抓住了我。我没有任何 Microsoft 产品。【参考方案2】:

未定义行为 = 错误

不可能证明一个项目没有错误。即使是最好的程序员也会制造错误。即使是最好的代码审查也无法消除项目中的所有错误。不,通过代码检查或任何其他方式消除某个规模项目中的所有 UB 是不现实的。您最好的选择是审查代码并尽可能多地消除。

改变您对 UB(错误)的看法:如果您在重新设计过程中遇到错误,这是一件好事!您处于删除一个 UB 的最佳位置。

不要因为害怕 UB 而保留旧的编译器。使用可用的最新最好的编译器重新编译项目。编译器也可能有错误。较新的编译器将生成更好、更健壮的代码。较新的编译器会产生更好的警告。使用所有可能的警告-Wall

消除编译器产生的所有警告。每一个警告都是有原因的,它突出了一个问题。如今,“误报”的可能性非常小。甚至对于 MSVC 也是如此(我不是在谈论像 VC 2005 之前那样真正的旧编译器)

使用静态代码检查器 (Cppcheck)。它可以指出代码的常见问题。

使用custom rule set 作为代码检查器。它将帮助您使代码达到某种标准。

如果可能,请使用其他编译器(GCC、Clang)编译项目,以获取这些编译器的警告。

不要链接到旧的目标文件。这会产生比你认为它避免的更多的问题

【讨论】:

在 VS 上是 /W4 而不是 -Wall @MikeMB,看here 它 -Wall 即使在 Visual Studio 上。 Visual Studio 上的所有选项都可以以/- 为前缀 是的,有 Wall,但是在代码库上,如果从一开始就没有用它编译,那它是非常没用的,因为你被埋在一堆误报之下。它实际上是在激活所有警告(甚至比 gcc/clang 上的 -Wextra 更具侵略性) @MikeMB:忽略警告不是一个好习惯。一次解决数百个问题是不可能的,但有时您可以使用 Wall 进行编译,修复一两个软点,然后回到更安静的选项来处理更紧迫的工作。 @MartinBonner,您的话在上帝的耳中:bool 是一种内在的 C++ 类型。 BOOLint (VS 2015) 的特定于 Windows 的 typedef。查看this page 会发现bool 的大小为1 字节,而BOOL 的大小为4。如果您的假设在未来某处失败,请不要感到惊讶。看this article【参考方案3】:

正如其他人所说:首先,尝试找出错误,而不是隐藏它们。

    第一个也是最简单的措施是将警告级别设置为/W4(您可以尝试Wall,但由于这会产生大量噪音(例如来自标准头文件),它通常只是有帮助的如果你知道你的代码的某个部分有错误) 使用静态分析器 - 您可以从内置的 Code Analysis 工具开始,然后使用外部工具(对于非平凡的项目,这些工具通常更难以正确设置)。 编写大量测试并确保您正在执行边缘情况 - 这就是 UB 通常潜伏的地方。 如果可能,请尝试在 clang 下编译项目(或其中的一部分)并激活不同的清理程序(特别是 UndefinedBehaviorSanitizer),这将进一步检测您的代码以检查 UB(仅在您有测试时才有帮助锻炼那个UB) 在不同的优化级别和标志组合下测试您的代码(在 VS 中,尤其是 _ITERATOR_DEBUG_LEVEL 有助于发现越界错误)

我想说任何重要的代码库都可能包含未定义的行为。那个特定的程序员有什么特别之处?如果他/她容易出现一种特殊的 UB,那么你可以集中精力解决这个问题。

【讨论】:

以上是关于处理可能包含未定义行为的项目的主要内容,如果未能解决你的问题,请参考以下文章

C中未定义的行为实际上会发生啥[关闭]

Oracle 默认行为排序

将数组作为结构访问与未定义的行为

不允许在解除分配时尝试加载视图控制器的视图,这可能会导致未定义的行为

C++ 中的这种未定义行为是不是从悬空指针调用函数

对象可能是“未定义” - 在包含未定义检查的过滤器之后