如何找到某些宏的来源

how to find the source of some macros

本文关键字:何找      更新时间:2023-10-16
有很多

地方可以定义宏。当我们在自己的项目中定义宏时,很容易找到它们的定义位置。但是当我尝试学习一些著名的开源项目时,我经常被一个问题所困扰:在哪里可以找到宏的来源,如果我不能得到它的定义,我就不会理解其中的一些(例如,其中一些可以通过它们的名字来猜测)。例如,来自 Apache 的一些声明:

#if defined(__osf__) && defined(__alpha),
#elif defined(__NSIG)

至于我的知识,我知道宏有一些可能的起源地:

  1. 从这个项目本身,在一些源文件中(这是最简单的,因为我们可以通过一些工具找到它)
  2. 从某个 3rd lib 的一些头文件中,我们可以 grep 它
  3. 从C/C ++标准头文件(它们在Linux中的什么位置?
  4. 从操作系统(它们在Linux中在哪里?
  5. 由配置工具自动生成(很苦,我不知道)
  6. 从像 gcc/g++ 这样的编译器工具中,或者在 makefile 中我们可以定义一些宏

我有一些问题要咨询:

  1. 如何区分操作系统定义的和 GCC/G++ 定义的以及配置工具生成的宏? 它们分别具有一些特征吗?
  2. 如何找到操作系统或标准C或编译器定义的源代码? 例如,使用grepfind实用程序
  3. 如果通过梳理整个机器(cd /;grep __strange___ -r)找不到一个宏,例如__strange___,这意味着什么?

感谢您告诉区分它们的原理和方法,并找到它们的来源!

找出宏定义位置的一种简单、快速的方法是重新定义宏并检查编译器的警告/错误消息。

#include <windows.h>
#define min(a,b) nonsense
mintest.cpp(3) : warning C4005: 'min' : macro redefinition
    C:ProgrammeMicrosoft SDKsWindowsv6.0Aincludewindef.h(194) : see previous definition of 'min'
  1. 如何区分操作系统定义的和 GCC/G++ 定义的以及配置工具生成的宏? 它们分别具有一些特征吗?

绝大多数是在某个头文件中定义的。 gcc -dN -E在这里可以提供帮助。警告:如果使用此方法,则需要使用相同的包含路径、相同的-D<name>命令行选项、相同的环境变量(如 CPATH、...)调用gcc -dN -E,就像将源文件编译为对象文件时所做的那样。

  1. 如何找到由操作系统或标准C或编译器定义的源代码? 例如,使用 grep 或查找实用程序

RTFM。阅读精细手册。

  1. 如果通过梳理整个机器(cd /;grep __strange___ -r)找不到一个宏(例如__strange__)是什么意思?

这可能只是意味着您的计算机上未定义该符号。假设有问题的代码来自某个开源包,该包针对不同系统、不同编译器的大量内容,其中一些不太符合C++标准。典型的方法是在代码的关键部分使用 #ifdef __some_bizarre_os__。该符号只会在运行Bizarre OS的机器上定义 - 而不是在你的机器上。

不幸的是,这不是唯一的情况。即使您的 grep 在任何地方都找不到它,也可以很好地定义该符号。makefile 可以连接两个字符串(-D__strange__,以形成编译器的单个命令行参数。选项-D__strange__可能隐藏在生成文件使用的环境变量之一中。某些项目授权的~/.tcshrc文件可能非常复杂。

更新
gcc -dM -E显示了宏的定义,但没有显示宏的定义位置。更好的选择是使用 gcc -dN -E,然后过滤掉不以初始#开头的行。

gcc 编译器定义的宏可以通过以下方式显示

gcc  -dM -E a.c

除此之外,它们都来自包含的文件和来源。

如果找不到宏,则意味着条件将被评估为 false。

您也可以使用 -v 选项,它会显示它在哪里找到其默认包含目录。

要找出宏来自哪个文件,请执行以下操作:

gcc -E $your_compile_options $your_c_file | 
egrep "^# " | grep -v '<'| cut -f 2 -d '"' | 
sort | uniq |
while read line
    do
            grep -l $your_macro $line
    done

正如其他人所说,应该使用具有-d选项之一的gcc找出宏的定义位置。这些选项不是由gcc -v --help输出的,因此必须使用手册第 3.11 章 搜索-dCHARS 来阅读它们。

最后我的步骤是:

  1. 使用gcc ... -E -dD
  2. 在输出文件中查找定义
  3. 向后搜索#(哈希和空格)以显示文件

看起来这些宏是编译常量。

最好使用此类宏告诉编译器这部分代码需要编译,而这部分代码不进行编译。

如果您无法在项目工作区中搜索它们,则应浏览程序流程,并确定应用程序需要哪一部分代码,并定义相应的宏。

例如;

#ifdef (_CASE1_)
...
...
...
#elif (_CASE2_)
...
...
...
#endif

现在在上面的例子中,如果你的应用程序需要_CASE1_涵盖的代码,那么你必须定义_CASE1_,例如 #define _CASE1_

希望对...

如果你正在研究一些开源项目,我假设你已经设置好了它,以便你可以构建它。选择一个包含要查找的宏的文件,并使用编译器生成预处理的文件。实际选项取决于您使用的编译器,它是 -E 表示 gcc,您可以在此处找到更多信息。

请注意,您可能必须使用项目的生成系统来实际编译文件,并查看预处理器运行成功所需的选项。

获得预处理文件后,只需搜索宏即可。有一些预处理器选项可以为每个包含的文件生成路径名。

更新:这种方法显然不起作用,因为您的宏是由处理器扩展的。因此,除非你能认识到它的扩展形式或效果,否则它几乎没有用处。

可能更有用的是让编译器打印出包含文件的确切顺序。这是在 gcc 中使用 -H 选项实现的。

您应该使用像 Eclipse 这样的 IDE,您只需右键单击宏并单击"打开声明",它就会将您发送到文件并行定义的宏。

有时有些宏甚至没有在头文件中定义,因为它们是作为 gnu 编译器的标志给出的(例如:-DMYMACRO)。

我通常觉得 Vim 足够了:CTRL-W CTRL-I[I 是我的最爱。(这些是所有类型的标识符,CTRL-W CTRL-D[D仅适用于宏。您可以为可用命令的整个列表键入 :help include-search

要使这些工作,您应该在 Vim 中正确设置path选项。键入 :set path? 以查看您的当前设置。运行 gcc -v -E -x c++ - </dev/null ,查找#include <...> search starts here:,然后复制目录并将它们添加到您的path(在您的.vimrc中)。(我所做的是提取目录并将它们存储在.bash_profile中的环境变量中,并在我的.vimrc中引用该目录,如set path=$INCLUDE_PATH。您可能还需要将任何特定于项目的包含目录添加到path中。