这个问题有几个单独的部分:如何告诉编译器/链接器生成和保存“调试信息”(源代码和目标代码之间的映射),如何告诉编译器以不同的方式编译代码以使调试更容易(想想 assert() 和 #ifdef _DEBUG),以及链接到项目中的预编译库是否包含调试信息。
-Zi(指示 CL 编译器生成调试信息的标志)相当于 gcc 的 -g 标志。
(还有其他形式的 -Z 选项:-ZI 如果您想要 Visual Studio IDE 中的“编辑并继续”支持,但如果您使用的是 IDE,您可能正在使用它的编译器接口设置而不是直接操作它们,如果您想要旧的 CodeView 格式的调试信息,则使用 -Z7;每当我直接调用 CL 时,我想要的始终是 -Zi。)
请注意,使用 -Zi(或 -ZI)选项通常会为每个目录生成一个 .pdb 文件,但是当您将代码链接在一起时,它可能来自不同 .pdb 文件中表示的 .obj 文件,而您还想将这些单独的 .pdb 文件组合成一个主文件,代表您链接在一起的代码——这就是链接器的 -debug 开关的用途。
另请注意:这听起来可能违反直觉,但请始终使用 -Zi(用于 CL)和 -debug(用于 link.exe)。即使是你要发布的代码。它不会增加可执行文件的大小,也不会向客户泄露机密,因为调试信息位于单独的 .pdb 文件中(您不会将其发送给客户)。如果您有任何机会必须对其进行调试,那么您将需要 .pdb。 (-Zi 甚至与优化不兼容,尽管 -ZI 是。所以你可能想用 -ZI 编译你的“调试”版本,而你的“发布”版本用“-Zi -O2”编译。)
至于库:您不需要严格匹配 C 运行时库的调试/发布属性与您的代码是否包含调试信息,但这通常是一个好主意——如果您要调试您希望能够调试所有项目,如果您不打算调试它,则不需要额外的重量。使用给定库的调试/发布版本不会影响它是否具有可用的调试符号(希望,如果编译该库的人理解我在上一段中提出的观点),但它会影响诸如断言和额外的 #ifdef _DEBUG 之类的东西该库中的代码。
这适用于您链接的所有库,尤其是 C 运行时库——微软在 malloc() 和 free() 中添加了额外的错误检测代码。因此,如果您的项目中的任何内容都使用了 CRT 库的调试风格,那么所有这些都应该是。
在我看来,/M 选项(/MTd 和 /MDd)既奇怪又神奇——它们只是幕后发生的一组复杂的其他事情的别名。以 /MDd 为例,记录为“定义 _DEBUG、_MT 和 _DLL 并导致您的应用程序使用运行时库的调试多线程和 DLL 特定版本。它还导致编译器将库名称放置为 MSVCRTD。 lib 到 .obj 文件中。”在这里,它同时影响预处理器(定义 _DEBUG 和一些其他预处理器符号)和链接器(它实际上在源代码中放置了 #pragma 注释(链接器))。如果您关心正在发生的事情并且不理解它,这可能会导致真正的问题——我已经看到很多不使用 IDE 的项目都陷入了关于 msvcrt.lib 和 msvcrtd.lib 的警告中被链接等等。当你了解如何安全地使用这些(/M 选项)时,你真的不再需要它们了!我更喜欢明确说明:直接在我需要的地方指定“-D _DEBUG”,指定要显式链接的库(并使用 -nodefaultlib),然后就不需要 /M 选项了。