学习预处理
预处理
- 1.预定义符号
- 2.#define定义常量
- 3.#define定义宏
- 4.带有副作用的宏参数
- 5.宏替换的规则
- 6.宏函数的对比
- 7.#和##
- 8.命名约定
- 9.#undef
- 10.命令行定义
- 11.条件编译
- 12.头文件的包含
- 1.预定义符号
- 2.define定义常量
- 3.#define定义宏
- 4.带有副作用的宏参数
- 5.宏替换的规则
- 6.宏函数的对比
- 7.#和##
- 7.1#运算符
- 7.2##运算符
- 8.命名约定
- 9.#undef
- 10.命令行定义
- 11.条件编译
- 12.头文件的包含
- 12.1头文件被包含的方式
- 12.2嵌套文件包含
1.预定义符号
2.#define定义常量
3.#define定义宏
4.带有副作用的宏参数
5.宏替换的规则
6.宏函数的对比
7.#和##
8.命名约定
9.#undef
10.命令行定义
11.条件编译
12.头文件的包含
1.预定义符号
C语言设置了一些预定义符号,可以直接使用,预定义符号也是在预处理阶段处理的。
__FILE__ //进行编译的文件
__LINE__ //文件当前的行号
__TIME__ //文件被编译的时间
__DATA__ //文件被编译的日期
__STDC__ //如果编辑器遵循ANSI C,其值为1,否则未定义
//例子:
printf("file:%s line:%d
",__FILE__,__LINE__);
(续行符 ,后面不能有空白)
2.define定义常量
基本语法:
#define name stuff
例子:
#define MAX 100
#define reg register
#define do_forever for(;;)
//对于for(),初始值、判断条件、更新都可省略,但判断条件要省略的话,就进入了死循环
#define DEBUG_PRINT printf(" ")
//若define最后加上; , 会把;也替换进去:
#define MAX 100;
//MAX == 100; 而不是100
3.#define定义宏
#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常叫做宏,或定义宏。
下面是宏的声明方式:
#define name( parament -list) stuff
其中parament-list是一个由逗号隔开的符号表,他们可能出现在stuff中。
注意:
- 参数列表的左括号必须与name紧邻,如果二者之间有任何空白的存在,在参数列表就会解释为stuff的一部分。
- 使用宏传递参数时,他不会计算传递的表达式的值,而是将表达式直接替换。
- 要考虑计算顺序问题,多加括号。
代码示例:
#define SQUARE(x) x * x
int main()
{
int a = 5;
printf("%d
", SQUARE(a + 1));//11 -- 将5+1替换到x * x为a + 1 * a + 1
return 0;
}
#define SQUARE(x) ((x) * (x))
int main()
{
int a = 5;
printf("%d
", SQUARE(a + 1));//36
return 0;
}
4.带有副作用的宏参数
副作用就是表达式求值的时候出现的永久性后果。
例如:x++
对于MAX宏更要注意:
#define MAX(a,b) ((a) > (b) ? (a) : (b))
...
x=5;
y=8;
z=MAX(x++,y++);
//z=((x++) > (y++) ? (x++) : (y++))
5.宏替换的规则
在程序中扩展#define定义符号和宏时,需要涉及几个步骤:
- 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,他们首先被替换。
- 替换文本后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。
- 最后,再次对结果文件进行扫描,看看他是否包含任何由#define定义的符号。如果有,重复上述步骤。
注意:
1.宏参数和#define定义中可以出现其他#define定义的符号。但对于宏,不能出现递归。
2.当预处理器搜索#define定义的符号的时候,字符串常量的内容不被搜索。
6.宏函数的对比
对于简单的运算,如:
#define MAX(a,b) ((a) > (b) ? (a) : (b))
为什么不用函数来完成?
原因有二:
1.宏比函数在程序规模和速度方面更胜一筹。
2.宏是直接替换的,参数是类型无关的。
和函数相比宏的劣势:
- 每次使用宏时,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度。
- 宏没法调试。
- 宏由于类型无关,不严谨。
- 宏可能带来运算优先级问题,要多加括号。
宏可以做函数做不到的事情。比如:宏的参数可以是类型,但函数做不到。
代码示例:
#define MALLOC(num,type)
(type*)malloc(num * sizeof(type))
int main()
{
int* p = (int*)malloc(10 * sizeof(int));
int* ptr = MALLOC(10, int);
//int* ptr = (int*)malloc(10*sizeof(int));
return 0;
}

7.#和##
7.1#运算符
#运算符将宏的一个参数转换为字符串字面量。它仅允许出现在带参数的宏的替换列表中,它所执行的操作可以理解为“字符串化”。
代码示例:
int a = 10;
#define PRINT(n) printf("the value of " #n " is %d",n)
int main()
{
PRINT(a);
return 0;
}
7.2##运算符
##把位于两边的符号合成一个符号,被称为记号粘合。
当我们写一个函数求2两个数的较大值的时候,不同的数据类型就得写不同的函数。
代码示例:
int int_max(int x, int y)
{
return x > y ? x : y;
}
float float_max(float x, float y)
{
return x > y ? x : y;
}
使用宏后:
#define GEBERTIC_MAX(type)
type type##_max(type x,type y)
{
return x>y?x:y;
}
GEBERTIC_MAX(int);
GEBERTIC_MAX(float);
int main()
{
int m = int_max(2, 3);
printf("%d
", m);
float fm = float_max(3.5f, 4.5f);
printf("%f
", fm);
return 0;
}
8.命名约定
- 宏名全大写
- 函数名不全大写
9.#undef
这条指令用来移除一个宏定义。
10.命令行定义
C编辑器提供一种能力,允许在命令行中定义符号,用于启动编译过程。
如下面代码在命令行中定义ARRAT_SIZE以满足内存要求:
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("
");
return 0;
}
//编译指令 gcc -D ARRAY_SIZE=10 programe.c
11.条件编译
在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为有条件编译指令。
常见条件编译指令,代码示例:
1.
#if 常量表达式
// ...
#endif
2.多个分支的条件编译
#if 常量表达式
//...
#elif
//...
#else
//...
#endif
3.判断是否被定义
#if defined(X)/#ifdef X
#if !defined(X)/#ifndef X
//...
#elif defined(Y)...
//...
#endif
代码应用示例:
#define M 3
int main()
{
#if M==0
printf("ghehe
");
#elif M==1
printf("haha
");
#elif M==2
printf("OK
");
#else
printf("bit
");
#endif
return 0;
}
#define MAX 1
int main()
{
# ifdef MAX
printf("dede
");
#endif
#if defined(MAX)
printf("dede
");
#endif
#ifndef MAX
printf("hehe
");
#endif
#if !defined(MAX)
printf("hehe
");
#endif
return 0;
}
12.头文件的包含
12.1头文件被包含的方式
1,本地文件包含:#include “filename”
查找策略:现在源文件目录下查找,如果没找到,就和库函数一样去标准位置(安装路径)去找。
2.库文件包含: #include
查找策略:直接去标准路径下去查找。
对于库文件也可以用“”形式包含,但是这样做效率降低,并且不容易区分是库文件还是本地文件。
12.2嵌套文件包含
#include指令可以使另外一个文件被编译,所以如果重复包含,对编译的压力就比较大。
方法:条件编译
每一个头文件开头写:
#ifdef __TEST_H__
#define __TEST_H__
//文件内容
#endif
或者
#pragma once










