C语言源文件要经过编译、链接才能生成可执行程序:
- 编译(Compile)会将源文件(
.c
文件)转换为目标文件。对于VC/VS,目标文件后缀为.obj
;对于GCC,目标文件后缀为.o
。 - 链接(Link)是针对多个文件的,它会将编译生成的多个目标文件以及系统中的库、组件等合并成一个可执行程序。
但是在编译和连接之前,通常还有一个步骤——预处理
C预处理器不是编译器的组成部分,它是编译过程中一个单独的步骤。C预处理器只是一个文本替换工具, 它们会指示编译器在实际编译之前完成所需的预处理。通常将把C预处理器(C Preprocessor)简写为 CPP。
所有的预处理器命令都是以井号(#)开头。它必须是第一个非空字符,为了增强可读性,预处理器指令应从第一列开始。
define
:定义宏include
:包含一个源代码文件undef
:取消已定义的宏ifdef
:如果宏已经定义,则返回真ifndef
:如果宏没有定义,则返回真if
:如果给定条件为真,则编译下面代码else
:#if 的替代方案elif
:如果前面的 #if 给定条件不为真,当前条件为真,则编译下面代码endif
:结束一个 #if……#else 条件编译块error
:当遇到标准错误时,输出错误消息pragma
:使用标准化方法,向编译器发布特殊的命令到编译器中
__DATE__
:当前日期,一个以 "MMM DD YYYY" 格式表示的字符常量。__TIME__
:当前时间,一个以 "HH:MM:SS" 格式表示的字符常量。__FILE__
:这会包含当前文件名,一个字符串常量。__LINE__
:这会包含当前行号,一个十进制常量。__STDC__
:当编译器以ANSI标准编译时,则定义为 1。__STDC_VERSION__
:为C99时设置为199901L__VA_ARGS__
:用于定义带参数的宏时说明参数是可变参数
务必要注意: 预处理器只是一个文本替换工具
#include
是文件包含命令,主要用来引入对应的头文件。#include的处理过程很简单,就是将头文件的内容插入到该命令所在的位置,
从而把头文件和当前源文件连接成一个源文件,这与复制粘贴的效果相同。
包含标准库的头文件用尖括号,包含自定义的头文件用双引号。
#define
用于定义宏,宏定义是预处理命令的一种,它允许用一个标识符来表示一个字符串。一般用于定义常量或者表达式
宏定义注意:
- 宏定义是用宏名来表示一个字符串,在宏展开时又以该字符串取代宏名,这只是一种简单的替换。字符串中可以含任何字符,可以是常数,也可以是表达式,预处理程序对它不作任何检查,如有错误,只能在编译已被宏展开后的源程序时发现。
- 宏定义不是说明或语句,在行末不必加分号,如加上分号则连分号也一起替换。、
- 宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束。如要终止其作用域可使用#undef命令。
- 宏定义与typedef是不一样的。 typedef是类型定义,是有类型的。宏定义虽然也可表示数据类型, 但毕竟是作字符代换。在使用时要分外小心,以避出错。
能够根据不同情况编译不同代码、产生不同目标文件的机制,称为条件编译。条件编译是预处理程序的功能,不是编译器的功能。
if、ifdef、ifndef
等预处理指令用于实现条件编译。条件编译在预处理阶段完成的,多余的代码以及所有的宏都不会参与编译,不仅保证了代码的正确性,还减小了编译后文件的体积。
#line
用于重置由 __LINE__
和__FILE__
宏报告的行号和文件名,使用方式为
#line 1000//重置行号
#line 2000 "aa.c"//重置行号和文件名
#error
指令使预处理器发出一条错误信息,该消息包含指令中的文本。可能的话编译过程应该被终端。使用方式为
#if __STDC_VERSION__ == 1999901L
#error NOT C99
#endif