如何确定 foo.c 中的哪些预处理器宏源自 bar.h?

How can I determine which preprocessor macros in foo.c originate in bar.h?

我有两个 C 语言文件:foo.cbar.h(可能还有许多其他文件)。我想要在 foo.c 中使用并在 bar.h 中定义的所有预处理器宏的列表。

或者,如果这太难了,即使是出现在 foo.cbar.h.

我怎样才能得到它?

一个策略(公认的麻烦)可以是:

foreach 宏标识符 SOME_MACRO in bar.h, 运行

gcc -E -DSOME_MACRO=recognizable_value foo.c | grep recognizable_value

即预处理源并检测是否发生扩展。请注意,这不适用于仅在 #if 指令等中使用的宏。

您可以从 gcc 预处理选项的输出中拼凑出这些信息。

要获取文件实际使用的宏列表,您可以使用 -E -dU 选项,它会对文件进行预处理,并在第一个 use 处包含 #define 命令 任何宏。 (它还会为使用 #ifdef#if defined(...) 测试的未定义名称生成 #undef 命令。)(您必须使用 -E 选项——仅预处理——用于 -dU 妥善处理。)

由于 -dU 不抑制预处理输出,您需要通过仅查看 #define 指令来过滤它。对于某些应用程序,您可能还希望通过仅查看相关文件中的实际用途来进一步过滤它,因为报告也包括所包含文件对宏的使用。但在这种情况下,与 header 文件中实际定义的宏的交集可能就足够了。

因此要获取 file.c 中使用的宏列表:

gcc -E -dU file.c | grep -Eo '^#define [_A-Za-z][_A-Za-z0-9]*'

(grep -Eo 删除了宏的定义。)

您可以使用更慷慨的 grep 调用来近似 header 文件中实际定义的宏列表,如下所示:

grep -Eo '^\s*#\s*define\s+[_A-Za-z][_A-Za-z0-9]*' header.h

即使条件失败,它也会选取条件部分中定义的宏,并且会选取注释中看起来像 #define 指令的行。通常,这些都不会引起很多问题。

您可以使用 gcc 的 -E -dM-E -dD 选项来获取 header 中所有定义的列表,但这两者也会插入 [=58] 定义的宏=]s 包含在 header 中。 (-dM 还包括预定义的宏。)所以你真的需要做更多的工作来关注 header 文件实际定义的宏,除非你对定义为包含 header 文件的结果。

然后你只需要找到两个列表的交集。一种方法是将宏名称(awk '{print }')、sort -u两个列表独立提取然后合并,最后将它们都通过uniq -d传递,只看两个列表中的条目。 (以下都定义了 shell 函数 used_and_defined,您将调用 used_and_defined foo.c bar.h

used() {
  gcc -E -dU "" |
  grep -Eo '^#define [_A-Za-z][_A-Za-z0-9]*' |
  cut -f2 -d' ' |
  sort -u
}

defined() {
  grep -Eo '^\s*#\s*define\s+[_A-Za-z][_A-Za-z0-9]*' "" |
  awk '{ print  }' |
  sort -u
}

used_and_defined() {
  cat <(used "") <(defined "") | sort | uniq -d
}

或者您可以使用 awk

完成整个操作
used_and_defined() {
  awk '/^[[:space:]]*#[[:space:]]*define/ {
         gsub(/[ (].*/, "", );
         if (NR == FNR) ++macros[];
         else if (macros[]) print ;
       }' \
       <(grep -Eo '^\s*#\s*define\s+[_A-Za-z][_A-Za-z0-9]*' "") \
       <(gcc -E -dU "")
}

一个gcc-specific(但clang具有相同语义的相同选项)除了-E之外还使用-dD

gcc -E -dD -o foo.i [other options] foo.c

将在输出中保留 #define 行以及 # nnn "/path/to/file.h" 指令,以便您应该能够分辨出哪个宏属于哪个文件。如果你想提取来自 bar.h 的宏,这取决于你希望在 bar.h 中找到的宏数量,你最喜欢的编辑器的搜索命令可能就足够了,或者一个小的 awk/perl/python/...脚本会有所帮助。