1.预定义符号
- __FILE__:进行编译的源文件
- __LINE__:文件当前的行
- __DATE__:文件被编译的日期
- __TIME__:文件被编译的时间
- __STDC__:如果编译器遵循ANSIC,其值为1,否则未定义
这些预定义符号都是c语言内置的
eg:printf(“file:%s line:%d\n”, __FILE__, __LINE__);
2. #define
- #define定义标识符
- 语法:#define name stuff
- 注意:如果定义的stuff过长,可以分成几行书写,除了最后一行外,每行结束的后面都加上一个续航符 \
例子:
1. #define DEBUG_PRINT printf("file:%d\t line:%d\t \ date:%s\t time:%s\n", \ __FILE__,__LINE__, __DATE__<__TIME__) 2. #define CASE break;case //在写case的语句时候自动把break加上 3. #define do_forever for(;;) //用更形象的符号来替换一种实现 4. #define reg register //为register关键字创建一个简短的名字
- 我们可以注意到在上面的例子中,#define定义的标识符最后结尾都没有分号,这样是为了防止替换时出现语法错误
- #define定义宏
- #define机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏或者定义宏
- 申明方式:#define name(parament-list //是一个由逗号隔开的符号表,可能出现在stuff中) stuff
注意:参数列表的左括号必须与name紧邻;如果参数列表与左右括号之间有任何的空白存在,参数列表就会被解析为stuff的一部分,举个例子来说明一下:
#define SQUARE( x ) x * x int a = 5; printf("%d\n", SQUARE( a + 1 ) ); //替换后我们实际上想要的结果是36,但是打印出的却是11 printf("%d\n", a + 1 * a + 1); //所以产生的结果便是11 //解决方法:在宏定义上加上两个括号即可 #define SQUARE(x) (x) * (x) //替换后为 printf("%d\n", (a+1) * (a+1));
- 虽然我们使用了括号避免了上述出现的问题,但是还是可能会出现符号优先级的问题,还是举例来说明
#define DOUBLE(x) (x) + (x) int a = 5; printf("%d\n", 10 * DOUBLE(a)); //替换后为 printf("%d\n", 10 * (5) + (5) ); //我们期望打印出100,但是事实上打印出的是55,原因是乘法的优先级高于加法,所以我们可以在宏定义表达式两边加上一对括号即可 #define DOUBLE(x) ( (x) + (x) ) //替换后为 printf("%d\n", 10 * ((5) + (5)) );
所以我们应当在用于对数值表达式进行求值的宏定义都应该用上述方式加上括号,避免在使用宏定义的参数中的操作符或邻近操作符之间不可预料的相互作用
- 带副作用的宏参数
- #define MAX(a, b) ( (a) > (b) ? (a) : (b) )
- ... x = 5; y = 8; z = MAX(x++, y++); printf("x=%d y=%d z=%d\n", x, y, z);//输出的结果是什么?
- 这里我们得知道预处理器处理之后的结果是什么:
z = ( (x++) > (y++) ? (x++) : (y++));- 所以输出的结果是:
x=6 y=10 z=9
- #define替换步骤
- 在程序中扩展#define定义符号和宏时,有这么几个步骤
- 在调用宏时,首先要对参数进行检查,看看是否包含任何由#define定义的符号,如果有,他们首先被替换
- 替换文本随后被插入到程序中原来的文本位置。对于宏,参数名被他们的值替换
- 最后,再次对结果文件进行扫描,看看是否还有包含任何由#define定义的符号。如果有,就重复上述过程
- 注意:宏参数和#define定义中可以出现其他#define点翻译的变量,但是对于宏,不能出现递归;预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索
- 宏里面的#和##作用
#的作用:把一个宏参数变成字符串
int i = 10; #define PRINT(FORMAT, VALUE)\ printf("the value of "#VALUE" is "FORMAT \n", VALUE); PRINT("%d", i+3);
- 代码中的#VALUE会被预处理为"VALUE", 最终输出的结果为 the value of i+3 is 13
##的作用
- ##的作用是可以把位于它两边的符号合成一个符号
- 允许宏定义从分离的文本片段创建标识符
#define ADD_TO_SUM(num, value) sum##num += value; ADD_TO_SUM(5, 10); //作用就是给sum5加10
3.宏与函数的比较
- 函数
- 宏比函数在程序的规模和速度方面更胜一筹
- 函数的参数必须声明为特定的类型,所以函数只能在类型合适的表达式上使用;但是宏可以用来比较整型、长整型、浮点型等的类型。宏是类型无关的
- 宏
- 每次使用宏的时候,一份宏定义的代码将插入到代码中,会造成代码大幅度增加。除非宏比较短
- 宏无法进行调试
- 宏与类型无关,是优点也是缺点,不够严谨
- 宏稍不注意就会带来运算符优先级的问题,容易导致程序出错
- 命名约定
宏名全部大写,函数名不要全部大写
4.移除宏以及命令行定义
- 移除宏
- #undef name
- 这条指令用于移除一个宏定义,也就是说现存的一个名字需要重新被定义,那么它的旧名字首先要被移除
- 命令行定义
- 许多C 的编译器器提供了一种能力,允许在命令行中定义符号。用于启动编译过程
- 例如:当我们根据同⼀个源文件要编译出不同的一个程序的不同版本的时候,这个特性有点用 处。(假定某个程序中声明了一个某个长度的数组,如果机器内存有限,我们需要一个很小的数 组,但是另外一个机器内存大写,我们需要一个数组能够大写。)
#include <stdio.h> int main() { int array [ARRAY_SIZE]; int i = 0; for(i = 0; i< ARRAY_SIZE; i ++) { array[i] = i; } for(i = 0; i< ARRAY_SIZE; i ++) { printf("%d " ,array[i]); } printf("\n" ); return 0; } // 编译指令: gcc -DARRAY_SIZE=10 programe.c