C语言由源代码生成可执行程序的过程如下:
C源程序->编译预处理->编译->优化程序->汇编程序->链接程序->可执行文件
其中编译预处理阶段,读取C源程序,对其中的预处理指令(以#开头的指令)和特殊符号进行处理。或者说是扫描源代码,对其进行初步的转换,产生新的源代码提供给编译器。
预处理过程先于编译器对源代码进行处理,读入源代码,检查包含预处理指令的语句和宏定义,并对源代码进行转换。预处理过程还会删除程序中的注释和多余的空白字符。
一、预处理指令
在C语言的程序中包括各种以符号#开头的编译指令,这些指令称为预处理命令。预处理命令属于C语言编译器,而不是C语言的组成部分,通过预处理命令可扩展C语言程序设计的环境。
预处理指令是以#号开头的代码行,#号必须是该行除了任何空白字符外的第一个字符。
#后是指令关键字,在关键字和#号之间允许存在任意个数的空白字符,整行语句构成了一条预处理指令,该指令将在编译器进行编译之前对源代码做某些转换。
预处理指令主要有以下三种:
1)包含文件:将源文件中以#include格式包含的文件复制到编译的源文件中,可以是头文件,也可以是其它的程序文件。
2)宏定义指令:#define指令定义一个宏,#undef指令删除一个宏定义。
3)条件编译:根据#ifdef和#ifndef后面的条件决定需要编译的代码。
二、包含文件
当一个C语言程序由多个文件模块组成时,主模块中一般包含main函数和一些当前程序专用的函数。程序从main函数开始执行,在执行过程中,可调用当前文件中的函数,也可调用其他文件模块中的函数。
如果在模块中要调用其他文件模块中的函数,首先必须在主模块中声明该函数原型。一般都是采用文件包含的方法,包含其他文件模块的头文件。
文件包含中指定的文件名即可以用引号括起来,也可以用尖括号括起来,格式如下:
#include <文件名>
或
#include "文件名"
如果使用尖括号<>括起文件名,则编译程序将到C语言开发环境中设置好的include文件中去找指定的文件(/usr/include)。
因为C语言的标准头文件都存放在/usr/include文件夹中,所以一般对标准头文件采用尖括号;对程序员自己编写的文件,则使用双引号。
如果自己编写的文件不是存放在当前工作文件夹,可以在#include命令后面加在路径。
#include命令的作用是把指定的文件模块内容插入到#include所在的位置,当程序编译链接时,系统会把所有#include指定的文件链接生成可执行代码。
#include包含文件,可以是“.h”,表示C语言程序的头文件,也可以是“.c”,表示包含普通C语言源程序。
三、宏定义指令
使用#define命令并不是真正的定义符号常量,而是定义一个可以替换的宏。被定义为宏的标识符称为“宏名”。在编译预处理过程时,对程序中所有出现的“宏名”,都用宏定义中的字符串去代换,这称为“宏替换”或“宏展开”。
在C语言中,宏分为有参数和无参数两种。
1、无参数的宏
其定义格式如下:
#define 宏名 字符串
在以上宏定义语句中,各部分的含义如下:
# 表示这是一条预处理命令(凡是以“#”开始的均为预处理命令)。
define 关键字“define”为宏定义命令。
宏名 是一个标示符,必须符合C语言标示符的规定,一般以大写字母标识宏名。
字符串可以是常数,表达式,格式串等。在前面使用的符号常量的定义就是一个无参数宏定义。
注意:预处理命令语句后面一般不会添加分号,如果在#define最后有分号,在宏替换时分号也将替换到源代码中去。在宏名和字符串之间可以有任意个空格。
#define PI 3.141592
示例(book149.c)
/*
* 程序名:book149.c,此程序演示不带参数的宏。
* 作者:C语言技术网(www.freecplus.net) 日期:20190525
*/