__FILE__ __LINE__ __DATE__ __TIME__ __STDC__ |
//进行编译的源文件 //文件当前的行号 //文件被编译的日期 //文件被编译的时间 //如果编译器遵循ANSI C,其值为1,否则未定义 |
这些都是C语言内置的预处理符号。
所有的预处理指令都会在代码的预处理阶段被替换。
#define
语法:
#define name stuff
#define除了可以定义标识符之外还可以定义宏
下面是宏的申明方式:
#define name( parament-list ) stuff
例如:
#define MAX(x, y) (x)>(y)?(x):(y)
而宏的调用和函数是一样的
编辑
但是函数是传值宏是替换
#define的替换规则:
- 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换。
- 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。
- 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程
- 宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归。
- 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。
这里有一点需要特别注意不然非常容易出错,因为宏是直接进行替换的如图,
可是函数是先计算出一个值然后将值拷贝过去所以如果传的参是一个表达式那么宏的出错率要远远高于函数。比如下面这个我们的初心是计算4*4=16,可是因为宏的特性程序实际计算的是3+1*4=7;而函数的结果就非常容易预估了。
所以在定义宏时应该注意:
- 不要吝啬括号最好对每个变量都使用;
- 因为宏不方便调试所以计量不要将宏定义的过于复杂。
#和##
平时的使用中如果我们运行下面的代码,我们想让输出结果随着变量名的改变而改变比如把a改为b输出结果就变为b=10,这在函数中是无论如何都不会实现的:
这时就要使用#了
可以看到在宏中使用’#‘符号就可以做到。
而如果在宏中使用‘##’就可以将两个字符串进行合并。
条件编译
#if 常量表达式//常量表达式由预处理器求值 //... #endif 多个分支的条件编译 #if 常量表达式 //... #elif 常量表达式 //... #else //... #endif 判断是否被定义 #if defined(symbol) //可以缩写为: #ifdef symbol #if !defined(symbol) //可以缩写为: #ifndef symbol
宏和函数的对比
因为宏省去了函数调用和返回的时间所以宏的速度更快;
函数的参数必须声明为特定的类型所以函数只能在类型合适的表达式上使用。反之这个宏怎可以适用于整形、长整型、浮点型等可以用于>来比较的类型。宏是类型无关的。
每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度。
宏是没法调试的。
宏由于类型无关,也就不够严谨。
宏可能会带来运算符优先级的问题,导致程容易出现错。
一般来讲函数的宏的使用语法很相似。所以语言本身没法帮我们区分二者。
那我们平时的一个习惯是:
- 把宏名全部大写
- 函数名不要全部大写