预处理过程:宏

本文学习C语言中宏地基本概念,以及编译器在预处理过程中对宏的处理。Hightec编译器版本是tricore v4.9.1.0。

1 宏定义

在C语言中,宏定义随处可见。比方说Autosar的代码中,或者Simulink生成的代码中,都包含了大量的宏定义。通常来说,在C文件的最开始处,或者头文件中会定义一个宏,然后在后面的代码中如果遇到了同样的片段,预处理器就把这段名字替换成定义好的内容。

宏定义分为两种,对象宏函数宏。后文会利用编译器来研究这两种宏的基本用法。

2 对象宏

对象宏是一个简单的标识符,之所以称为类对象,是因为它看起来像是使用它的代码中的数据对象,它们通常用来给一个数字取个名字,这样就便于阅读理解。

2.1 简单的对象宏

宏定义用 #define 来定义,例如下面就是定义了一个名为 BUFFER_SIZE 的宏,它背后代表的是数组1024.

#define BUFFER_SIZE 1024

可以创建一个简单的C文件,再调用编译器对它进行预处理,就可以直观地看到编译器是怎么处理宏定义的。

首先,创建一个 demo.c 源文件如下;
在这里插入图片描述
C文件中定义了一个宏 BUFFER_SIZE ,数值是1024。后面又把这个宏作为 malloc 函数的参数传入。

通过如下命令行,对 demo.c 文件进行预处理;

tricore-gcc -E demo.c -o demo.i

然后打开预处理输出的 demo.i 文件,如下图;
在这里插入图片描述
可以看出,在 demo.i 文件中不再有 #define 字段,并且malloc函数中的参数由 BUFFER_SIZE 变成了它所代表的数值——1024。也就是说,预处理器将 BUFFER_SIZE 这个宏“展开”到了代码中。

2.2 多行宏定义

宏定义在 #define 这一行末尾就结束了,如果需要在多行中定义一个宏,就需要用反斜杠,例如如下的宏定义。

#define NUMBERS 1, \
2, \
3
int x[] = {
    
     NUMBERS };

也可以用编译器对他处理一下进行验证。demo.c文件改成如下图所示。
在这里插入图片描述
处理后的i文件就是如下所示。
在这里插入图片描述

2.3 宏定义的位置

C源文件中的宏定义一般都是在最开始的地方,因为这样写,预处理器就能一开始就知道有哪些宏。如果宏定义在代码的中间,在处理到这一行之前,预处理器是不知道这个宏的。具体可以看一下下面的例子。

在代码中写下如下图的定义;
在这里插入图片描述
通过命令行对其进行预处理,结果输出的i文件如下;
在这里插入图片描述
可以注意到,由于 #define 是在foo赋值之后的,所以foo等号右边的X没有被预处理器替换为数字4,在后面的编译过程中会因为没定义X而报错。这就提醒了我们,宏定义要写在代码的最开头部分。

2.4 嵌套宏定义

如果宏定义中套了好几层,预处理器也会一层一层地展开宏定义。例如下图中,定义了两个宏。
在这里插入图片描述
图中先把 TABLESIZE 这个宏定义为 BUFSIZE ,再把 BUFSIZE 这个宏定义为1024。用预处理器输出i文件如下。
在这里插入图片描述
最终,等号右边地宏被展开了两次,变成了数字1024。

3 函数宏

除了对象宏以外,C语言中还可以定义一种类似于函数定义的宏,被称为函数宏。函数宏和对象宏的一个重要区别在于,看名字里面有没有括号。

3.1 简单的函数宏

函数宏也是用 #define 来定义的,例如下面的指令把 lang_init() 函数定义为 c_init() 。

#define lang_init() c_init()

需要注意的是,预处理器在代码中只有遇到了带括号的函数字段,才会展开函数宏。如果只是名字和函数宏名字相同的变量,就不会做处理。这一点也可以调用预处理器来验证一下。

首先,将 demo.c 文件修改如下;
在这里插入图片描述
通过预处理器对其处理,输出的i文件如下;
在这里插入图片描述
可以看出,函数宏 lang_init() 被展开为 c_init() ,但是赋值x的语句右边的 lang_init 由于没有带括号,就不会被处理。

另外还需要指出,在 #define 指令中,括号必须紧跟在函数名称后面,否则预处理后的结果就不是上图所示的那样展开。

3.2 函数宏的参数

函数宏也可以像真正的函数一样,在括号中带有参数。如果有多个参数,就用逗号隔开。

例如下图所示的函数宏,用来比较两个数的大小,并返回较小的那个数字。
在这里插入图片描述
通过预处理器对其处理,输出的i文件如下;
在这里插入图片描述
在展开函数宏的时候,把其中的参数也一起展开了。例如第2行的 x = min(a, b); ,第一个参数a对应着宏定义的参数X,第一个参数b对应着宏定义的参数Y。展开后,a和b也处于X和Y对应的位置。

4 总结

本文学习了一些C语言宏定义的基本用法,并且用编译器输出i文件对其进行验证,有了直观的认识。在编译手册中,宏定义还有很多复杂的用法和注意点,博主认为不需要一次性全搞懂,等到遇到了对应的问题后再进行查阅即可。

>>返回个人博客总目录

猜你喜欢

转载自blog.csdn.net/u013288925/article/details/114895695