天天看点

为什么调试器会显示错误的函数

有时候在解决一些问题的时候需要使用到调试,这个场景很常见,我们在代码的某个地方设置一个断点,然后步进到一个函数内部单步调试,但是你会发现,调试器会显示你在另外一个函数中。这是咋回事儿?我们先来看看下面的一段代码。

为什么调试器会显示错误的函数

当你步进到调用p->GetValue()的地方时,你会发现调试器显示当前在Class1::GetQ中。那么,发生了什么呢?

这是因为,从链接器的视角来看,在代码生成阶段,链接器合并了所有相同的函数,如下图所示:

为什么调试器会显示错误的函数

请注意,在对象代码级别上,这两个函数是完全相同的。(请注意,两个函数在目标代码级别是否相同很大程度上取决于你使用的编译器版本以及优化标志。当你使用模板时,不同函数的相同代码生成频率非常高。)

因此,链接器说:”好吧,拥有两个相同的功能有什么意义? 我将只保留一份并用它来代表 Class1::GetQ 和 Class2::GetValue。”

为什么调试器会显示错误的函数

请注意,这两个函数已合并:函数的地址是相同的。 只是这一段代码有两个名字。 因此,当调试器看到你已跳转到 0x010010d6 时,它不知道应该使用哪个名称,所以它只是选择了其中的一个。

这就是为什么看起来你跳错了函数。

这就是所谓的“相同COMDAT折叠”(identical COMDAT folding),你可以将 /OPT:NOICF 标志传递给链接器来禁止此优化。

总结

在开发TopomelBox的过程中,有一天突发奇想,想去了解所有编译开关的意思。

有些很容易理解,比如各种头文件目录等,但有些链接器开关实在是难以理解,今天的这个”COMDAT Folding”就是其中的一个。

搞开发这条路,还是任重而道远啊。

最后

Raymond Chen的《The Old New Thing》是我非常喜欢的博客之一,里面有很多关于Windows的小知识,对于广大Windows平台开发者来说,确实十分有帮助。