【问题标题】:C macros: extract/dump all evaluated compile time #definesC 宏:提取/转储所有评估的编译时间#defines
【发布时间】:2021-11-30 08:37:57
【问题描述】:

我正在尝试找到从源代码中提取评估定义列表的最佳方法。
我发现了一些关于如何在编译时评估宏的问题,但我特别希望提取每个#define 的结果以转储到文件中。

这里是问题的一些示例代码:

#define VALUE1 0x01
#define VALUE2 0x10
#define VALUE3 VALUE1 | VALUE2

int main(void) {
    printf("VALUE01: 0x%02X\n", VALUE1);
    printf("VALUE02: 0x%02X\n", VALUE2);
    printf("VALUE03: 0x%02X\n", VALUE3); //prints evaluated VALUE3
}

从技术上讲,示例代码可以实现我正在寻找的内容,但是很麻烦,并且需要编写脚本来生成和维护。理想情况下,已经有一个我忽略的编译器标志可以做到这一点,因为它已经必须评估宏,但我找不到。 是否有任何其他编译器工具可用于实现此目的?

This 回答的问题是我找到的最接近的答案,并为我提供了转储所有定义的能力,但它会转储源代码中看到的未评估形式:

$ gcc -dM -E test.c | grep "VAL"
#define VALUE1 0x01
#define VALUE2 0x10
#define VALUE3 VALUE1 | VALUE2
.... (and all the other preprocessor defines)

【问题讨论】:

  • 要清楚 #define VALUE3 VALUE1 | VALUE2 并不意味着 VALUE3 的计算结果为 VALUE1 | VALUE2。这意味着令牌VALUE3 将被字符串VALUE1 | VALUE2 替换,其中VALUE1VALUE2 将替换为它们各自的定义。所以VALUE3 将是0x01 | 0x10(而不是0x11)。在最终评估中如何处理它取决于在代码中使用它的上下文。这不包括 rici 在他的回答中引用的额外复杂性。
  • 假设没有额外的复杂性(没有运行时依赖、重新定义等),是否有任何直接的方法可以评估为#define VALUE3 0x11?我问,因为在我的示例中,编译器似乎在“评估”#define。执行objdump -d <object file> 时,我可以在反汇编中看到生成的0x11
  • 评估 VALUE30x11 只有知道您将 VALUE3 扩展的上下文限制为特定情况才有意义。例如,表达式x = VALUE3 肯定可以替换为x = 0x11。但是,如果您有x = VALUE3 - 1,这与x = 0x10 不同,因为+ 的运算符优先级高于|。它不涉及任何重新定义或复杂性。这只是上下文。因此,假设VALUE3 通常评估为0x11 并不是很有用。
  • 总的来说,我完全同意。但我确实有一个非常具体的用例,想知道VALUE3 的评估结果。似乎除了创建一个脚本来提取#defines 并像示例中那样评估它们之外,没有直接的方法。

标签: c gcc


【解决方案1】:

-dM 表示是唯一正确的答案,因为编译器不会“评估宏”;它只是在每个替换站点用其替换文本替换每个宏。因此,宏没有明确的“评估形式”;每种用法可能不同。

请务必记住,宏替换不会评估任何内容。它实际上只是一个文本替换。评估通常仅在程序运行时发生,尽管如果结果不依赖于任何可能在运行时更改的内容(例如,如果表达式中的所有值都是文字常量),编译器可能会自行进行评估。在第二种情况下,通常称为“常量折叠”,计算发生在宏替换完成很久之后。

对于一个简单(且丑陋)的示例,大致基于您的代码:

#include <stdio.h>
#define VALUE3 VALUE1 | VALUE2
int main(void) {
  #define VALUE1 0x01
  #define VALUE2 0x10
    printf("%d, %d\n", VALUE3,
  #undef  VALUE1
  #define VALUE1 0x02
                       VALUE3);
}

将打印17, 18

VALUE1的扩展也随着源码的扩展而变化。)

【讨论】:

  • 让我们假设第二种情况,在编译时评估我的所有#defines(没有运行时依赖项,没有重新定义等)。有没有办法利用常量折叠来评估 #defines 并将其转储到文件中?
  • @jens:基本上,没有。正如我所说,常量折叠发生在宏被替换之后(如果发生的话),并且被替换的宏不会留下任何痕迹。因此,即使您可以检测到常量 6 是从 2*3 计算出来的,也无法判断 2*3 来自宏扩展。 (这不是二元选择;可能只有一部分来自宏。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-09-27
  • 2012-02-01
  • 2021-02-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多