处理可能包含未定义行为的项目
Deal with project which may contain undefined behaviour
我想咨询在这种情况下如何处理。
假设我有一个运行良好的大型 C++ 项目。
我怀疑此代码中可能存在一些 UB(因为在同一作者编写的不同项目中我发现了 UB)。
现在,假设我需要向该项目添加新功能。
恐怕是因为:
- 如果我使用新的编译器重新编译,如果代码中已经存在 UB,这会增加发生 UB 的风险。 (例如,新编译器可能不适用于旧编译器 的 UB。
通过目测消除这个大型项目中的所有 UB 是否现实(在我开始添加新功能之前)??
如果没有,那我至少应该用相同版本的编译器编译吧? (如果有 UB,以减少出现问题的机会)。
项目在 Visual Studio 中完成,所以我不知道是否有目标文件,在这种情况下,我可以保留目标文件不变,只修改文件中需要添加内容的部分 - 因此再次将 UB 的风险降至最低。
遇到这种情况应该怎么办?我认为这可能是很常见的情况。
我喜欢 建议 我在添加新代码之前使用新编译器测试项目,但即便如此 - 我们知道测试可能不会揭示 UB,不是吗?
按顺序,我会:
- 用
-Wall
编译(/W4
给你 Windows 人)并修复错误。
- 如果还没有,请编写测试。
- 使用
valgrind
等工具检测问题并修复它们。
- 研究同步原语(如果使用),并尽可能使用现代范例。
- 记录代码并遵守风格指南。
我不会试图通过保留目标文件来避免问题。这是一个噩梦般的维护问题。
未定义的行为 = 错误
要证明一个项目没有错误是不可能的。即使是最好的程序员也会制造错误。即使是最好的代码审查也无法消除项目中的所有错误。不,通过代码检查或任何其他方式消除某种规模的项目中的所有 UB 是不现实的。您最好的选择是检查代码并尽可能多地消除。
改变你对UB(bugs)的看法:如果你在重新设计的过程中遇到了bug,那是件好事!您处于移除 1 个 UB 的最佳位置。
不要因为怕UB而保留旧的编译器。使用最新和最好的可用编译器重新编译项目。编译器也可能有错误。较新的编译器将生成更好、更健壮的代码。较新的编译器会产生更好的警告。使用所有可能的警告 -Wall
。
消除编译器产生的所有警告。每一个警告都是有原因的,它突出了一个问题。 "false positive" 现在的可能性很低。对于 MSVC 甚至是这样(我不是在谈论像 VC 2005 之前那样真正的旧编译器)
使用静态代码检查器 (Cppcheck)。它可以指出代码的常见问题。
使用 custom rule set 作为代码检查器。它将帮助您使代码达到某种标准。
如果可能,使用其他编译器(GCC、Clang)编译项目,只是为了得到这些编译器的警告。
不要 link 针对旧的对象文件。这会产生比您认为避免的更多的问题
正如其他人所说:首先,尝试发现错误,而不是隐藏它们。
- 第一个也是最简单的措施是将警告级别设置为
/W4
(您可以尝试 Wall
,但由于这会产生大量噪音(例如来自标准头文件),它通常只有在你知道你的代码的某个部分有错误时才有用)
- 使用静态分析器 - 您可以从内置
Code Analysis
工具开始,然后使用外部工具(对于重要项目,正确设置这些工具通常要困难得多)。
- 编写大量测试并确保您正在执行边缘案例 - 这通常是 UB 潜伏的地方。
- 如果可能,请尝试在 clang 下编译项目(或其中的一部分)并激活不同的消毒剂(特别是 UndefinedBehaviorSanitizer),这将进一步检测您的代码以检查 UB(仅供参考)如果你有测试来锻炼那个 UB)
- 在不同的优化级别和标志组合下测试您的代码(在 VS 中,尤其是
_ITERATOR_DEBUG_LEVEL
有助于发现越界错误)
我想说任何重要的代码库都可能包含未定义的行为。那个特定的程序员有什么特别之处?如果he/she容易出现一种特殊的UB,那么你可以把精力放在这方面。
我想咨询在这种情况下如何处理。 假设我有一个运行良好的大型 C++ 项目。
我怀疑此代码中可能存在一些 UB(因为在同一作者编写的不同项目中我发现了 UB)。
现在,假设我需要向该项目添加新功能。 恐怕是因为:
- 如果我使用新的编译器重新编译,如果代码中已经存在 UB,这会增加发生 UB 的风险。 (例如,新编译器可能不适用于旧编译器 的 UB。
通过目测消除这个大型项目中的所有 UB 是否现实(在我开始添加新功能之前)??
如果没有,那我至少应该用相同版本的编译器编译吧? (如果有 UB,以减少出现问题的机会)。
项目在 Visual Studio 中完成,所以我不知道是否有目标文件,在这种情况下,我可以保留目标文件不变,只修改文件中需要添加内容的部分 - 因此再次将 UB 的风险降至最低。
遇到这种情况应该怎么办?我认为这可能是很常见的情况。
我喜欢 建议 我在添加新代码之前使用新编译器测试项目,但即便如此 - 我们知道测试可能不会揭示 UB,不是吗?
按顺序,我会:
- 用
-Wall
编译(/W4
给你 Windows 人)并修复错误。 - 如果还没有,请编写测试。
- 使用
valgrind
等工具检测问题并修复它们。 - 研究同步原语(如果使用),并尽可能使用现代范例。
- 记录代码并遵守风格指南。
我不会试图通过保留目标文件来避免问题。这是一个噩梦般的维护问题。
未定义的行为 = 错误
要证明一个项目没有错误是不可能的。即使是最好的程序员也会制造错误。即使是最好的代码审查也无法消除项目中的所有错误。不,通过代码检查或任何其他方式消除某种规模的项目中的所有 UB 是不现实的。您最好的选择是检查代码并尽可能多地消除。
改变你对UB(bugs)的看法:如果你在重新设计的过程中遇到了bug,那是件好事!您处于移除 1 个 UB 的最佳位置。
不要因为怕UB而保留旧的编译器。使用最新和最好的可用编译器重新编译项目。编译器也可能有错误。较新的编译器将生成更好、更健壮的代码。较新的编译器会产生更好的警告。使用所有可能的警告
-Wall
。消除编译器产生的所有警告。每一个警告都是有原因的,它突出了一个问题。 "false positive" 现在的可能性很低。对于 MSVC 甚至是这样(我不是在谈论像 VC 2005 之前那样真正的旧编译器)
使用静态代码检查器 (Cppcheck)。它可以指出代码的常见问题。
使用 custom rule set 作为代码检查器。它将帮助您使代码达到某种标准。
如果可能,使用其他编译器(GCC、Clang)编译项目,只是为了得到这些编译器的警告。
不要 link 针对旧的对象文件。这会产生比您认为避免的更多的问题
正如其他人所说:首先,尝试发现错误,而不是隐藏它们。
- 第一个也是最简单的措施是将警告级别设置为
/W4
(您可以尝试Wall
,但由于这会产生大量噪音(例如来自标准头文件),它通常只有在你知道你的代码的某个部分有错误时才有用) - 使用静态分析器 - 您可以从内置
Code Analysis
工具开始,然后使用外部工具(对于重要项目,正确设置这些工具通常要困难得多)。 - 编写大量测试并确保您正在执行边缘案例 - 这通常是 UB 潜伏的地方。
- 如果可能,请尝试在 clang 下编译项目(或其中的一部分)并激活不同的消毒剂(特别是 UndefinedBehaviorSanitizer),这将进一步检测您的代码以检查 UB(仅供参考)如果你有测试来锻炼那个 UB)
- 在不同的优化级别和标志组合下测试您的代码(在 VS 中,尤其是
_ITERATOR_DEBUG_LEVEL
有助于发现越界错误)
我想说任何重要的代码库都可能包含未定义的行为。那个特定的程序员有什么特别之处?如果he/she容易出现一种特殊的UB,那么你可以把精力放在这方面。