预处理指令
C源码在进行编译前会先经过预处理,预处理指令均以#开头,结尾没有分号(;)不是C语句。预处理器采用“语言符号”的分析方式,将空格作为区分标志之间的符号,一行作为一条指令,“”可以将预处理指令延伸到下一行。
一、宏定义
1.不带参数的宏定义
(1)不带参数的宏定义就是用一个标识符(宏名)来代表一个字符串。它的一般形式为
#define Macro Str
在预处理的时候程序中的宏名Macro被替换为字符串Str,这个过程称为宏展开。
(2)#define指令出现在程序中函数的外面,宏名的有效范围为该指令行起到本源文件结束或#undef。
(3)宏展开只是简单的字符串替换,简单宏常用于定义常量,宏没有类型,也没有优先级的概念,使用定义常量主要用于指定数组长度 #define ayyLength 256 ,建议尽量使用const或enum代替宏定义常量 const int arrLen 256; 。建议不使用宏定义类型 #define Status int 而是用typedef关键字 typedef Status int; 。
(4)宏不是C语句不能加分号,否则会将分号一起代入。考虑到优先级问题,其中表达式也可能需要括号。
(5)宏代替的字符串可以是常量也可以表达式,格式描述符,语句(甚至是它们的一部分)等任何C程序中出现的字符串。 当然,宏代替的字符串中也可以包含已定义的宏名。
#undef macro
#undef指令可以终止宏名的定义。
2.带参数的宏定义
(1)
#define Macro(argus) str
对带参数的宏展开 是将带实参的宏 按照#define指令行中按从左到右的顺序进行置换。宏名和带参数的括号之间不能加空格,否则将成为无参数宏,空格后的每一个字符都将作为替代字符串的一部分。
(2)带参数的宏不是函数,它只是进行简单字符代换 。定义宏时参数和字符串可以是任意的,但在定义时要注意标识符不能出现重名【#define Macro(Macro) str】(利用代码块作用域)。在实际调用时参数可能是单个数据对象也可能是表达式,由于宏只进行简单文本代换考虑到优先级和结合性的问题,建议在定义宏时将参数用圆括号括起以作为一个独立单位。
(3)含参宏类似于inline函数,其调用也是采用传址的方式(真正的在调用点嵌入函数代码)。宏无类型,其参数也没有类型,只是一个符号代表,含参宏可以作为模板函数(C++引入,不同类型的参数可以使用同一段函数体)。
(4)宏与函数相比没有参数传递和返回值的限制使用更加自由灵活,而函数相对独立便于完成较复杂的任务。函数调用需较多时间处理内存等,而宏不需要。
3.宏定义中的运算符:
(1)对程序作预处理前,编译器会进行翻译处理。编译器首先把源码中的字符映射到源字符集。然后编译器查找反斜线后紧跟换行符(这里指按下回车键在源码中产生的换行符而非转义字符' ')的实例并删除这些实例。所以反斜线加回车键可以将宏定义扩展到多行。
(2)#运算符与参数结合可以那参数名转换为相应字符串。例如:x是宏的形参,实参为1时#x将被替换为"1"(字符串),x将被替换为1(数值常量)。
(3)##运算符可以把两个语言符号组合成一个语言符号。例如:n是宏的形参,实参为1时,x##n将变为标识符x1,如果无##编译器将把xn当做一个语言符号,在宏参数中中无法找到于是不进行替换。
二、文件包含处理
#include<filename> 或 #include"filename"
- 尖括号中的文件名优先在编译器安装目录中查找(通常是标准库),双引号中的文件优先在工作目录中查找(自定义库)。
- 文件包含处理是指将另一个源文件的全部内容包含进来,即将另外的文件内容包含到本文件之中,插入到当前位置,代替预处理指令然后进行编译得到一个目标文件。
- 头文件中只有函数声明和宏定义,真正的实现在库中。在链接(linking)时,库才被链接进来。
- 这种常用在文件头部的被包含文件被称为头文件(header),常以.h作为后缀。当然不用.h作为后缀用.c作为后缀也是可以的,但是用.h更能表示此文件的性质。
- 同样,#include指令不一定要出现在文件首部。应当注意,被包含文件修改后,凡包含此文件的所有文件都要全部重新编译。文件包含处理是将要包含的文件的内容代替预处理指令,成为源文件的一部分。
三、条件编译
条件编译使得程序中的一部分内容只在满足一定条件时才进行编译或不进行编译。
1.ifdef指令
若指定的标识符已经被#define指令定义过,则对程序段1进行编译否则对程序段2进行编译。可以用于程序调试等。
也可以用
#ifdef Block #endif
2.ifndef指令
与1正好相反,若标识符未被定义则编译程序段1,否则编译程序段2。
为了避免头文件重复包含,通常使用条件编译:
3.if指令
表达式为真时编译程序段1,否则编译程序段2。
不用条件编译指令而用if语句同样也可以实现,但是那样做目标程序长(所有语句都参加编译),运行时间长(在if语句处需进行逻辑判断)。