编译预处理指令
- #开头的是编译预处理指令
- 它们不是C语言的成分,但是C语言离不开它们
- #define用来定义一个宏
- #include用来包含一个头文件
C语言程序在编译之前会进行编译预处理,在编译预处理过程中会把所有的#define定义的宏进行替换。
C语言编译过程中会产生一些临时文件,在GCC编译器编译过程中如下:
main.c->main.i->main.s->main.o->a.out
- 由源代码main.c进行编译预处理得到main.i
- 由编译预处理后的代码文件main.i进行编译得到汇编代码文件main.s
- 汇编代码文件main.s做汇编得到目标代码文件main.o
- 目标代码文件main.o进行链接形成可执行文件a.out
#define
- #define <名字><值>
- 注意没有结尾的分号,因为不是C的语句
- 名字必须是一个单词,值可以是各种东西
- 在C语言的编译器开始编译之前,编译预处理程序会把程序中的名字换成对应的值,仅仅是做的完全的文本替换
- 使用gcc –save-temps可以保存编译过程中的临时文件
宏
- 如果一个宏的值中有其他的宏的名字,也是会被替换的
- 如果一个宏的值超过一行,最后一行之前的行末要加\
- 宏的值后面出现的注释不会被当作宏的值的一部分
没有值的宏
- #define _DEBUG
- 这类宏是用于条件编译的,后面有其他的编译预处理指令来检查这个宏是否已经被定义过了
- 在金山WPS实习开发WPS for Mac的时候遇到过,只有在Mac环境下编译才执行的一段代码
预定义的宏
- __func__ //函数的函数名
- __LINE__ //源代码文件的行号
- __FILE__ //源代码文件的文件名
- __DATE__ //编译时的日期
- __TIME__ //编译时的时间
- __STDC__ //判断该文件是不是标准C程序,当要求程序严格遵循ANSIC标准时该标识符被赋值为1
#include<stdio.h> int dxf(void); int main(int argc,int *argv[]) { dxf(); return 0; } int dxf(void) { printf("%s:%d\n",__FILE__,__LINE__);//输出源代码文件名和目前的行号 printf("%s\n",__func__);//输出函数名 printf("%s,%s\n",__DATE__,__TIME__);//输出编译的日期和时间 }
运行结果如下:
带参数的宏和像函数的宏
- #define cube(x) ((x)*(x)*(x))
- 宏可以带参数
由于预编译过程中#define仅仅是简单的文本替换,所以容易出现运算优先级问题,因此在定义带参数的宏的时候应该遵循一些原则
带参数的宏的原则
- 一切都要括号(整个值要括号,参数出现的每个地方都要括号)
- #define RADTODEG(x) ((x)*57.29578)
带参数的宏
- 可以带多个参数,如#define MIN(a,b) ((a)>(b)?(b):(a))
- 也可以组合嵌套使用其他宏
- 在大型程序的代码中使用非常普遍
- 部分宏会被inline函数替代
宏的缺点
- 宏的参数没有类型检查,处理不了特殊的输入,而内联函数inline的引入正是为了解决这个问题
宏展开的灵活运用
在Arduino的Ethernet库的w5100.cpp里有这样的函数调用:
writeTMSR(0x55);
但是遍寻整个.cpp和对应的w5100.h也找不到这个writeTMSR()函数,即使把所有的源代码目录拿来搜索一遍都没有。但是,编译显然是通过了的,那么,这个函数在哪里呢?
在w5100.h,我们发现了这样的代码:
#define __GP_REGISTER8(name, address) \ static inline void write##name(uint8_t _data) { \ write(address, _data); \ } \ static inline uint8_t read##name() { \ return read(address); \ }
于是,在w5100.h里接下去的代码:
__GP_REGISTER8 (TMSR, 0x001B); // Transmit memory size
在编译预处理后,就会被展开成为:
static inline void writeTMSR(uint8_t _data) { write(0x001B, _data); } static inline uint8_t readTMSR() { return read(0x001B); }
其中##是一种分隔连接方式,它的作用是先分隔,然后进行强制连接。
1 comment