目录

前言

一、预处理指令

二、包含文件

举个例子

运行效果

三、宏定义指令

预定义宏

举个例子

运行效果

无参数的宏

举个例子

运行效果

使用宏定义时需要注意的地方

带参数的宏

举个例子

运行效果

四、条件编译

#ifdef

举个例子

运行效果

#ifndef


前言

C语言由源代码生成可执行程序的过程:

C源程序->编译预处理->编译->优化程序->汇编程序->链接程序->可执行文件

  1. 其中编译预处理阶段,读取C源程序,对其中的预处理指令(以#开头的指令)和特殊符号进行处理
  2. 预处理过程先于编译器对源代码进行处理,读入源代码,检查包含预处理指令的语句和宏定义,并对源代码进行转换。
  3. 预处理过程还会删除程序中的注释和多余的空白字符

一、预处理指令

  1. 在C语言的程序中包括各种以符号 # 开头的编译指令,这些指令称为预处理命令。预处理命令属于C语言编译器,而不是C语言的组成部分,通过预处理命令可扩展C语言程序设计的环境
  2. 预处理指令是以#号开头的代码行,#号必须是该行除了任何空白字符外的第一个字符
  3. #后是指令关键字,在关键字和#号之间允许存在任意个数的空白字符,整行语句构成了一条预处理指令,该指令将在编译器进行编译之前对源代码做某些转换

重要的预处理器指令:

指令描述
#define定义宏
#include包含一个源代码文件
#undef取消已定义的宏
#ifdef如果宏已经定义,则返回真
#ifndef如果宏没有定义,则返回真
#if如果给定条件为真,则编译下面代码
#else#if 的替代方案
#elif如果前面的 #if 给定条件不为真,当前条件为真,则编译下面代码
#endif结束一个 #if……#else 条件编译块
#error当遇到标准错误时,输出错误消息
#pragma使用标准化方法,向编译器发布特殊的命令到编译器中

二、包含文件

  1. 程序从main函数开始执行,在执行过程中,可调用当前文件中的函数,也可调用其他文件模块中的函数
  2. 如果在模块中要调用其他文件模块中的函数,首先必须在主模块中声明该函数原型。一般都是采用文件包含的方法,包含其他文件模块的头文件
  3. 文件包含中指定的文件名即可以用 ” ” 括起来,也可以用

格式如下:

#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宏名字符串
  1. #表示这是一条预处理命令(凡是以“#”开始的均为预处理命令)
  2. define关键字 “define” 为宏定义命令
  3. 宏名是一个标示符,必须符合C语言标示符的规定,一般以大写字母标识宏名
  4. 字符串可以是常数,表达式,格式串等
  5. 预处理命令语句后面一般不会添加分号

举个例子

#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

使用宏定义时需要注意的地方

  1. 宏定义是宏名来表示一个字符串,在宏展开时又以该字符串取代宏名。这只是一种简单的代换,字符串中可以含任何字符,可以是常数,也可以是表达式,编译预处理时不会对它进行语法检查,如有错误,只能在编译已被宏展开后的源程序时发现
  2. 习惯上宏名用大写字母表示,以方便与变量区别。但也可以用小写字母

带参数的宏

#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
  1. 如果#ifdef后面的标识符已被定义过,则对 "程序段1" 进行编译;
  2. 如果没有定义标识符,则编译“程序段2”
  3. 一般不使用#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. 如果未定义标识符,则编译“程序段1”;否则编译“程序段2”
  2. 一般用 #ifndef 来防止头文件被重复包含

打开/usr/include/stdio.h文件,可以看到

第一条有效行的代码是

#ifndef_STDIO_H

接下来是

#define_STDIO_H1

最后一行是。

#endif

我们自定义自己的头文件时,也可以模仿这种写法