程序编写,编译第一步就是宏的声明,也称预处理器阶段,在编译之前,有删除注释,插入被#include指令包含的文件的内容、和#define所替换的符号等。
14.1 五种预处理符号
__FILE__ ''name.c'' 进行编译的源文件名
__LINE__ 25 文件当前的行号
__DATE__ ''Jan 14 2019'' 文件被编译的日期
__TIME__ ''11:32:07'' 文件被编译的时间
__STDC__ 1 如果编译器遵循ANSI C,其值就为1,否则未定义
FILE、LINE使用场景主要是在调试程序时
DATE、TIME使用场景:在编译后的程序中加入一个时间标志,区别程序的不同版本
STDC使用场景:当要求程序严格遵循ANSI C标准时,标识符__STDC__就会被赋值为1
14.2 宏
注意:在使用宏定义计算表达式如:
#define SQUARE(x) x*x
```
```
SQUARE(5) // 5*5
而在下面情况下会产生意外:
Error:
#define SQUARE(x) x*x
a = 5;
printf("%d\n",SQUARE(a+1)); // x*x预期结果为36
//而实际上 a+1*a+1 = 5+1*5+1 =11
Right:
#define SQUARE(x) (x) * (x)
a = 5;
printf("%d\n",SQUARE(a+1)); // (x)*(x)结果为(5+1)*(5+1) = 36 正确
所以在用宏定义一些计算表达式时需要注意符号之间的选择,否则会产生不可预期的错误。
14.2.1 #与##
’#‘的作用是将一个宏参数转换为一个字符串
#define PRINT(FORMAT,VALUE) \
printf( The value of "#VALUE" \
is "FORMAT"\n",VALUE)
'''
'''
PRINT("%d\n",x+3);
//将会输出 The value of x+3 is 25
// 将x+3以字符串的形式打印出
’##‘的作用是把位于它两边的符号连接成一个新的符号(这个新符号必须是有定义的,否则非法)
#define ADD_TO_SUM(sum_number, value) \
sum ## sum_number += value
'''
'''
'''
ADD_TO_SUM(5, 25);
//将sum_number = 5 连接到sum后, 再将 value = 25 赋值给 sum5
14.2.3宏与函数
(1)宏还可以用作简单的计算,如比较两个表达式中的较大值
#define MAX(a, b) ((a) > (b) ? (a): (b) )
其中用宏完成这个计算功能比函数完成产生的代码更少,所以在小型计算工作中,宏的计算速度和程序规模都比函数要好;
更重要的是,函数必须声明一种特定的数据类型,而宏却突破了这种限制,可以用整形,长整形,浮点数等,换句话说,宏是与类型无关的。而需要注意的是,每次使用宏时,一份宏定义代码的拷贝都将插入到程序中,除非程序很短,否则使用宏可能大幅度增加程序的长度。
(2)宏可以完成函数无法完成的任务
#define MALLOC(n, type) \
( (type *)malloc( (n) * sizeof( type ) ) )
//可以定义类型为参数的宏定义,而函数却无法完成
但不合理的使用宏参数将会产生副作用,比如:
x+1执行几百万次每次得到的结果都相同 而x++执行超过一次之后,每执行一次x的值将发生变化,到最后每个代码段中出现一次x之变化一次,直至最后x的值无法确定。
14.3 条件编译
这部分简单了解 首先给出语法形式:
#if constant-expression(常量表达式)
statements
#endif
14.4 文件包含
编译器支持两种不同类型的#include文件包含:
1. #include<filename>(处理调用函数库中的文件)
2.#include "filename" (处理本地头文件)
嵌套文件包含:在很多的大型程序中,往往包含了很多的头文件,而为了不重复调用同一头文件,可以用下面语法形式定义减少重复定义:
#ifndef __HEADERNAME_H
#define __HEADERNAME_H 1
#endif
14.7 警告的总结
1.不要在一个宏定义的末尾加上分号,使其成为一条完整的语句。
2.在宏定义中使用参数,但忘了在它们周围加上括号。
3.忘了在整个宏定义的两边加上括号。
14.8编程提示的总结
1.避免用#define指令定义可以用函数实现很长序列的代码。
2.在那些对表达式求值的宏中,每个宏参数出现的地方都应该加上括号,并且在整个宏定义的两边也加上括号。
3.采用命名约定,使程序员很容易看出某个标识符是否为#define宏。
5.只要适合就应该使用文件包含,不必担心它的额外开销。