预处理
1.预处理符号
C语言设置了一些预定义符号可以直接使用,预定义符号也是在预处理期间处理的。
2.#define定义宏常量
例如
#define n 10
#define print_time_date printf("%s %s",__TIME__,\
__DATE__)
#define 的内容可以分成几行写,除了最后一行,每行的后面都要加一个反斜杠(续行符) \
3.#define定义宏
#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏或定义宏。
宏的定义方式
其中的 parament-list 是一个由逗号隔开的符号表,它们可能出现在stuff中。
注意: 参数列表的左括号必须与 name 紧邻,如果两者之间存在任何空白,参数列表就会被解释为stuff中的一部分。
宏的参数不运算,直接替换
4.带有副作用的宏参数
当宏参数在宏的定义中出现超过一次时,如果参数带有副作用,那么这个宏可能出现危险,导致不可预测的后果。
例如
试比较以下两个结果
#include<stdio.h>
#define MAX(x,y) x>y?x:y
int main()
{
int x, y;
scanf("%d %d", &x, &y);
int m = MAX(x, y);
printf("%d", m);
return 0;
}
#include<stdio.h>
#define MAX(x,y) ((x++)>(y++)?(x++):(y++))
int main()
{
int x, y;
scanf("%d %d", &x, &y);
int m = MAX(x, y);
printf("%d\n", m);
printf("%d\n", x);
printf("%d\n", y);
return 0;
}
5.宏替换的规则
在程序中扩展 #define 定义符号时,需涉及几个步骤。
1.在调用宏时,先对参数经行检查,是否包含任何由#define 定义的符号。如果是,它们首先被替换。
2.替换文本随后插入到程序中原来文件的位置。对于宏,参数名被它们的值所替换。
3.最后再次对结果文件扫描,是否包含任何由#define定义的符号。如果有,就重复上述过程。
注意:
1.宏参数和#define 定义中可以出现其他 #define定义的符号。但是对于宏,不能出翔递归。
2.当预处理器搜索到#define定义的符号的时候,字符串常量的内容不易被搜索
6.宏与函数的对比
优势
宏通常用于简单的运算
1.用于调用函数和从函数返回的代码可能比实际执行这个小型计工作所需的时间更短。
2.函数只能在类型适合的表达式上使用,宏使用的类型更广
劣势
1.每次使用宏的时候,一份宏代码将插入程序中,通常会增加程序的长度。
2.宏是无法调试的。
3.宏与类型无关,不够严谨。
4.宏可能会带来运算优先级的问题,导致程序出错
7.#和# #
7.1 # 运算符
#运算符的操作可理解字符串化
将一个宏定义的参数转化为字符串字面量,它仅允许出现在带参数的替换列表中。
将#a 转化为 "a"
#define print(val,formate) printf("the value of "#val" is "formate"\n",val)
7.2 # # 运算符
把位于它两边的符号合成一个符号,它允许宏定义从分离的文本片段创建标识符。##被称为记号粘合这样的连接必须产生一个合法的标识符。否则其结果就是未定义的。
8.命名的约定
函数和宏的语法很像。语言本身很难区分,通常通过以下方法区分
宏名全部大写
函数不全部大写
9.#undef
移除一个宏定义
10.条件编译
调试性代码,删了可惜,保留碍事,可以选择条件编译
防止头文件重复包含
#ifndef __test__
#define __test__
#endif
也可以
#pragma once