nju-pa摸鱼记1-宏的妙用
生物化学反应迟钝
2022年01月31日 15:39

一、前言

上学期在课内学习了《计算机体系结构》,一直对最后一章老师讲到的硬件模拟器念念不忘,好奇它的工作原理。好巧不巧,某日在我刷github的时候南京大学的ics-pa项目(https://github.com/NJU-ProjectN/ics-pa出现在了我的首页推荐中,于是我便打算利用寒假的时间过一下这个项目,并通过B站的专栏记录一些新的知识或者是心得体会。

二、使用宏配置编译选项

在项目代码进行编译前,需要先使用

代码块
Shell
自动换行
复制代码
make menuconfig
复制成功

对项目进行配置,在勾选了一些选项退出后,menuconfig会根据之前的选项生成一些宏保存到若干文件中,供之后的.c文件或Makefile文件所包含(使用include)。这样,在C语言源文件或makefile脚本中通过对这些宏加以判断,就可以在编译时使用或抛弃某些特定的功能。

三、C语言中检测宏是否被定义

  • 在C语言源文件中,可以使用条件编译指令

代码块
clike
自动换行
复制代码
#ifdef XXX
	...
#endif
复制成功

将宏XXX对应功能的代码包围起来,如果在menuconfig中勾选了该宏所对应选项,则宏XXX会被定义,则最终这段代码也会被编译。

  • 除了使用条件编译指令,代码框架还提供了另一种方式检测某个宏是否被定义。之所以要使用另一种方式,是因为条件编译指令不能使用在宏定义中,而频繁地使用条件编译指令会使代码的可读性大打折扣。实现这个功能的关键代码包括:

代码块
clike
自动换行
复制代码
// macro.h
#ifndef _MACRO_H_
#define _MACRO_H_

#define str_temp(x) #x
#define str(x) str_temp(x)
#define isdef(macro) (strcmp("" #macro, "" str(macro)) != 0)

#endif
复制成功

    其中宏str(x)使用‘#’运算符将x转化为一个字符串。下面举一个例子来说明isdef这个宏是怎么工作的:

代码块
clike
自动换行
复制代码
// main.c
#include "macro.h"
#include <string.h>

#define FOO 123

int main()
{
	int a = isdef(FOO);
 	return 0;
}
复制成功

    经过预处理

代码块
Shell
自动换行
复制代码
gcc -o main.i -E main.c
复制成功

    main.i中的内容为:

代码块
Shell
自动换行
复制代码
// main.i
...
int main()
{
	int a = (strcmp("" "FOO", "" "123") != 0);
    return 0;
}
...
复制成功

    可以发现,strcmp函数的第一个参数对应isdef宏定义中的"&#​34; #macro,它直接将宏参数的名称转换为了字符串,而第二个参数对应"&#​34; str(macro),它先将FOO替换为123,然后将123转换为字符串。因此当这两个参数不同时,说明宏已经被定义过。

    通过上面的分析可以发现,宏isdef不能检测宏定义的值与宏定义名称相同的宏,比如说

代码块
Shell
自动换行
复制代码
#define FOO FOO
复制成功

    宏FOO在预处理时仍然被替换为FOO,最后将导致错误的结果。

四、参考资料

  • stackoverflow上关于如何判断宏是否被定义的讨论(https://stackoverflow.com/questions/26099745/test-if-preprocessor-symbol-is-defined-inside-macro)

  • 南京大学ics-pa在github上项目的链接(https://github.com/NJU-ProjectN/ics-pa)