debugging 为什么调试器不知道#define'd常量?

ha5z0ras  于 2023-02-23  发布在  其他
关注(0)|答案(7)|浏览(166)

在调试代码时,我经常想知道#define常量的值。但是调试器似乎不知道它们的值。这意味着我必须四处寻找include路径等来找到#define行。有什么技巧可以让这变得更容易吗?

**更新:**我不得不给Tony D打上绿色的勾,因为他详细地回答了标题问题,但是我也投票赞成使用const代替#define(我还测试了enum也能工作)。最后,使用F12查找原始的#define行是另一个好主意。
**2023年更新:**在我最初提出这个问题的七年之后,我注意到Visual Studio 17.5.0终于允许您看到#define的扩展

6yjfywim

6yjfywim1#

不管谷歌花了10分钟,Visual Studio似乎都不支持这一点。
有些编译器确实尝试过这样做,但这是有原因的,它有点脆弱/尽力而为...
首先,我们要重新梳理并推翻常见但虚假的解释:有一个 * 概念上 * 截然不同的预处理步骤,它在"正确的"编译开始之前用文本替换预处理器宏的使用,但实际上没有理由预处理阶段不能向前传递#define#undef ine语句的记录,以便包含在其他调试信息中。
痛苦之处在于,直觉上通常认为的"#define-ing一个预处理器常量"并不像...

const Some_Type some_identifier = some_value;

...因为后者在应用程序的命名空间层次结构中有一个特定的位置,并且实际上不能更改,而一个#define可以被#undef-inine和-#define d任意次,这样某个特定代码行的预处理器宏的"值"可以取决于该行是如何包含在翻译单元中的:* * 每个翻译单元中同一行的每次包含都可能具有该宏的不同值(或没有值)**。
因此,在调试代码时显示宏的"值"是有问题的--它只能保证在特定的 * 翻译单元 * 中的特定行有一个值,但程序员通常希望调试器根据 * 源文件 * 中的行来显示和导航程序。
考虑:
use_x.h

class T ## X {
    void g() { ... }
};

一些_应用程序. cc

#define X 2783
#include "use_x.h"
#undef X
#define X 2928
#include "use_x.h"
void f() {
    const int last_x = X;
}

如果您的调试器正在单步调试上面的f(),那么X可以说是2928,但是如果您正在单步调试g()的某个版本呢?调试器将很难理解类名和用于创建它的X的值之间存在某种联系,或者很难确定以某种其他方式显示什么......
更糟糕的是,如果您将鼠标悬停在某个宏上,同时停在其他行(可能是从不同的翻译单元链接进来的),调试器可能无法知道您是对执行所传递的该宏的最后一个值感兴趣,还是对继续执行时可能具有的下一个值感兴趣(如果它甚至可以预测任何可能首先发生的分支),或者调试器停止所在行的宏值(如果有)。
因此,一些工具链(编译器/调试信息编码/调试器)并不接受这种混乱。更好的工具可能会跟踪简单的情况,但对于过于复杂而无法分析的情况,可能什么也不显示--或者显示一系列可能的值。显示错误的值比什么都不显示要糟糕得多。
这在调试器中没有帮助,但是当需要检查宏值/替换时,您可以尝试cl /E(对于GCC/clang,选项的-E)为翻译单元提供预处理输出-然后您可以看到预处理器进行了哪些替换。

093gszye

093gszye2#

宏在编译前由预处理器求值和解析,这不仅仅是因为编译后的程序不知道它们的值:编译后的程序根本不包含它们的踪迹。没有什么可供调试器检查的。
将宏看作代码生成工具,一切都将变得清晰。
有些编译器确实有一个标志,你可以设置它来保留各种预处理器定义的值,并将它们列在一个特殊的区域,与你的可执行文件一起用于调试目的。不过,我不知道如何在VS中实现这一点。我会使用相关的开关来 only 运行预处理器,然后检查结果。

slhcrj9b

slhcrj9b3#

'#Define' d常量对于调试器是未知的,因为它们在编译前进行预处理,并且它们在代码中的外观被替换为值。
如果你想要一个常量值,为什么不使用const呢?这样你就可以在调试器中看到这个值,并且强制编译器检查你错误地试图改变代码中的值的任何地方。#define没有这样的安全性。

vcudknz3

vcudknz34#

关于“Visual Studio”,#define X 3在预处理期间被解析,并且X的所有外观都被替换为3。因此,当您将鼠标移动到它上面时,调试器不会显示它的值,就像在任何其他具有“硬编码”值的语句中一样。

int res = y + 3;

如果你把鼠标移到3上,它不会在浮动窗口中显示3。

dwbf0jvd

dwbf0jvd5#

为什么它们不被知道的原因已经得到了解答. I I I I.我真的感受到了你的痛苦。这就是为什么我开始的每个项目都要跟踪include路径,并且在编译之前,我会用grep将这些路径放在一个列出定义的文本文件中。我在调试的时候会把它放在手边。我想这是一个技巧:)
另外:也许真的很有帮助..我希望:)我不再使用Visual Studio自己,所以我不是100%肯定,如果它是可能在2015年,但:- 在配置属性-〉C / C++ -〉预处理器中将preprocess to a file设置为yes- visual studio曾经生成一个.i文件,我希望2015年仍然这样做,下次你编译你的程序时,因为这是预处理器的输出-打开这个.i文件,你至少可以看到你的宏的实际扩展值是什么,并在调试时在监 windows 口中输入此值。
这只会花费你一个额外的编译,但帮助你第二次左右,节省你宝贵的时间,你可以花在其他事情比文件夹潜水:)

apeeds0o

apeeds0o6#

在要了解其值的行上选择感兴趣的宏。
按F12键。
这将跳转到它的定义(编译器最好能计算出:给定的代码行可以具有相同#define!的多个定义)。

pbgvytdp

pbgvytdp7#

#define one 1

这只是文本替换。即使编译器也不知道代码中这个“1”后面是什么。
宏在编译/链接阶段之前由预处理器评估。
有关编译步骤的详细信息:How does the compilation/linking process work?

相关问题