目录
什么是预处理指令
C语言源文件要经过编译、链接才能生成可执行程序:
编译会将源文件(.c文件)转换为目标文件。对于 VC/VS,目标文件后缀为.obj;对于GCC,目标文件后缀为.o。编译是针对单个源文件的,一次编译操作只能编译一个源文件,如果程序中有多个源文件,就需要多次编译操作。
链接是针对多个文件的,它会将编译生成的多个目标文件以及系统中的库、组件等合并成一个可执行程序。
在编译之前对源文件进行简单加工的过程,就称为预处理(即预先处理、提前处理)。
1.预处理主要是处理以#开头的命令,例如#include <stdio.h>等。预处理命令要放在所有函数之外,而且一般都放在源文件的前面。
2.预处理是C语言的一个重要功能,由预处理程序完成。当对一个源文件进行编译时,系统将自动调用预处理程序对源程序中的预处理部分作处理,处理完毕自动进入对源程序的编译。
3.编译器会将预处理的结果保存到和源文件同名的.i文件中,例如 main.c 的预处理结果在 main.i 中。和.c一样,.i也是文本文件,可以用编辑器打开直接查看内容。
4.C语言提供了多种预处理功能,如宏定义、文件包含、条件编译等,合理地使用它们会使编写的程序便于阅读、修改、移植和调试,也有利于模块化程序设计。
头文件包含
包含一个头文件,到当前源文件中立即在指令下一行的位置。
- #include <文件名> (1)
- #include “文件名” (2)
-
在系统目录下搜索文件
-
在用户自定目录下搜索文件,如果未找到再到(1)下找
宏定义
预定义符号
__FILE__ //进行编译的源文件
__LINE__ //文件当前的行号
__DATE__ //文件被编译的日期
__TIME__ //文件被编译的时间
__STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义
这些预定义符号都是语言内置的。 举个栗子:
printf("file:%s line:%d\n", __FILE__, __LINE__);
#define
语法: #define name stuff
举个栗子:
#define MAX 1000
#define reg register //为 register这个关键字,创建一个简短的名字
#define do_forever for(;;) //用更形象的符号来替换一种实现
#define CASE break;case //在写case语句的时候自动把 break写上。
// 如果定义的 stuff过长,可以分成几行写,除了最后一行外,每行的后面都加一个反斜杠(续行符)。
#define DEBUG_PRINT printf("file:%s\tline:%d\t \
date:%s\ttime:%s\n" ,\
__FILE__,__LINE__ , \
__DATE__,__TIME__ )
小问题:在define定义标识符的时候,要不要在最后加上 ; ?
#define MAX 1000;
#define MAX 1000
//有什么区别
尝试运行一下下列例程
if(condition)
max = MAX;
else
max = 0;
采用第一个#define时,程序会报错,因为在预处理时,1000后面的;也被替换过来了!
#define 替换规则:
1. 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先 被替换。
2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。
3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上 述处理过程。
#和##
#运算符,通常称为stringifie(字符串化)运算符,使得在它后面的变元转换成带双引号的串
#include <stdio.h>
#define mkstr(s) #s
int main(void) {
printf(mkstr(I Like C));
return 0;
}
##操作符,称为 pasting(粘贴,链接)运算符,用于连接两个符号(实际用的很少)
#define ADD_TO_SUM(num, value) \
sum##num += value;
...
ADD_TO_SUM(5, 10);//作用是:给sum5增加10.
#undef
这条指令用于移除一个宏定义。
#undef NAME
//如果现存的一个名字需要被重新定义,那么它的旧名字首先要被移除。
#if......#endif
常见的条件编译指令