目录
前言
一、预处理指令
二、包含文件
举个例子
运行效果
三、宏定义指令
预定义宏
举个例子
运行效果
无参数的宏
举个例子
运行效果
使用宏定义时需要注意的地方
带参数的宏
举个例子
运行效果
四、条件编译
#ifdef
举个例子
运行效果
#ifndef
前言
C语言由源代码生成可执行程序的过程:
C源程序->编译预处理->编译->优化程序->汇编程序->链接程序->可执行文件
- 其中编译预处理阶段,读取C源程序,对其中的预处理指令(以#开头的指令)和特殊符号进行处理
- 预处理过程先于编译器对源代码进行处理,读入源代码,检查包含预处理指令的语句和宏定义,并对源代码进行转换。
- 预处理过程还会删除程序中的注释和多余的空白字符
一、预处理指令
- 在C语言的程序中包括各种以符号 # 开头的编译指令,这些指令称为预处理命令。预处理命令属于C语言编译器,而不是C语言的组成部分,通过预处理命令可扩展C语言程序设计的环境
- 预处理指令是以#号开头的代码行,#号必须是该行除了任何空白字符外的第一个字符
- #后是指令关键字,在关键字和#号之间允许存在任意个数的空白字符,整行语句构成了一条预处理指令,该指令将在编译器进行编译之前对源代码做某些转换
重要的预处理器指令:
指令 | 描述 |
---|---|
#define | 定义宏 |
#include | 包含一个源代码文件 |
#undef | 取消已定义的宏 |
#ifdef | 如果宏已经定义,则返回真 |
#ifndef | 如果宏没有定义,则返回真 |
#if | 如果给定条件为真,则编译下面代码 |
#else | #if 的替代方案 |
#elif | 如果前面的 #if 给定条件不为真,当前条件为真,则编译下面代码 |
#endif | 结束一个 #if……#else 条件编译块 |
#error | 当遇到标准错误时,输出错误消息 |
#pragma | 使用标准化方法,向编译器发布特殊的命令到编译器中 |
二、包含文件
- 程序从main函数开始执行,在执行过程中,可调用当前文件中的函数,也可调用其他文件模块中的函数
- 如果在模块中要调用其他文件模块中的函数,首先必须在主模块中声明该函数原型。一般都是采用文件包含的方法,包含其他文件模块的头文件
- 文件包含中指定的文件名即可以用 ” ” 括起来,也可以用
格式如下:
#include"文件名"#include
- 如果使用括起文件名,则编译程序将到C语言开发环境中设置好的 include文件中去找指定的文件(/usr/include)
- C语言的标准头文件都存放在/usr/include文件夹中,所以一般对标准头文件采用;对自己编写的文件,则使用 ” “
- #include包含文件,可以是 “.h”,表示C语言程序的头文件,也可以是”.c”,表示包含普通C语言源程序
举个例子
我们之前写的 test34.c 和 _public.h 和 _public.c ,不知道大家还有没有印象
_public.h
#include#include#includeint min(const int i1,const int i2);int max(const int i1,const int i2);
_public.c
#include "_public.h"int min(const int i1,const int i2){ if (i1i2) return i1; return i2;}
test34.c
#include "_public.h"int main(){ int x,y,imin,imax; x=100; y=200; imin=min(x,y); imax=max(x,y); printf("imin=%d,imax=%d\n",imin,imax);}
运行效果
三、宏定义指令
使用 #define 命令并不是真正的定义符号常量,而是定义一个可以替换的宏。被定义为宏的标识符称为“宏名”。在编译预处理过程时,对程序中所有出现的“宏名”,都用宏定义中的字符串去代换,这称为“宏替换”或“宏展开”
预定义宏
ANSI C 定义了许多宏。可以使用这些宏,但是不能直接修改这些预定义的宏
宏 | 描述 |
---|---|
__DATE__ | 当前日期,一个以 “MMM DD YYYY” 格式表示的字符常量 |
__TIME__ | 当前时间,一个以 “HH:MM:SS” 格式表示的字符常量 |
__FILE__ | 这会包含当前文件名,一个字符串常量 |
__LINE__ | 这会包含当前行号,一个十进制常量 |
__STDC__ | 当编译器以 ANSI 标准编译时,则定义为 1 |
举个例子
#include int main(){ printf("当前文件名 :%s\n", __FILE__ ); printf("当前日期 :%s\n", __DATE__ ); printf("当前时间 :%s\n", __TIME__ ); printf("当前行号 :%d\n", __LINE__ ); printf("ANSI :%d\n", __STDC__ );}
运行效果
无参数的宏
其定义格式如下:
#define宏名字符串
- #表示这是一条预处理命令(凡是以“#”开始的均为预处理命令)
- define关键字 “define” 为宏定义命令
- 宏名是一个标示符,必须符合C语言标示符的规定,一般以大写字母标识宏名
- 字符串可以是常数,表达式,格式串等
- 预处理命令语句后面一般不会添加分号
举个例子
#include #define PI 3.141592int main(){ printf("PI is %lf\n",PI);}
运行效果
执行预编译指令 gcc -E -o test100.E test100.c,得到 test100.E 文件
用vim打开test100.E文件,按 shift+g 跳到最后一行
可以看到PI被替换成 3.141592
使用宏定义时需要注意的地方
- 宏定义是宏名来表示一个字符串,在宏展开时又以该字符串取代宏名。这只是一种简单的代换,字符串中可以含任何字符,可以是常数,也可以是表达式,编译预处理时不会对它进行语法检查,如有错误,只能在编译已被宏展开后的源程序时发现
- 习惯上宏名用大写字母表示,以方便与变量区别。但也可以用小写字母
带参数的宏
#define命令定义宏时,还可以为宏设置参数。与函数中的参数类似,在宏定义中的参数为形式参数,在宏调用中的参数称为实际参数。对带参数的宏,在调用中,不仅要宏展开,还要用实参去代换形参
带参宏定义的一般形式为:
#define宏名(形参表)字符串
在定义带参数的宏时,宏名和形参表之间不能有空格出现,否则,就将宏定义成为无参数形式,而导致程序出错
举个例子
#include #define MAX(x,y) (x>y)" /> 执行预编译指令 gcc -E -o test101.E test101.c,得到 test101.E 文件
带参数的宏不容易理解,简单了解一下即可
四、条件编译
条件编译有多种格式,在这里只介绍最常用的两种格式 #ifdef 和 #ifndef
#ifdef
#ifdef命令的使用格式如下:
#ifdef标识符程序段1#else程序段2#endif
- 如果#ifdef后面的标识符已被定义过,则对 "程序段1" 进行编译;
- 如果没有定义标识符,则编译“程序段2”
- 一般不使用#else及后面的 "程序2"
举个例子
#include #define Linuxint main(){ #ifdef Linux printf("这是Linux系统\n"); #else printf("这不是Linux系统\n"); #endif}
运行效果
执行预编译指令 gcc -E -o test102.E test102.c,得到 test102.E 文件
#ifndef
而#ifndef的意义与#ifdef刚好相反,其格式如下:
#ifndef标识符程序段1#else程序段2#endif
- 如果未定义标识符,则编译“程序段1”;否则编译“程序段2”
- 一般用 #ifndef 来防止头文件被重复包含
打开/usr/include/stdio.h文件,可以看到
第一条有效行的代码是
#ifndef_STDIO_H
接下来是
#define_STDIO_H1
最后一行是。
#endif
我们自定义自己的头文件时,也可以模仿这种写法