C语言编译预处理详解

目录

前言

一、预处理指令

二、包含文件

举个例子 

运行效果

三、宏定义指令

预定义宏

举个例子

运行效果

无参数的宏

举个例子 

运行效果 

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

带参数的宏

举个例子 

运行效果

四、条件编译

#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
#include

int 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__ );

}

运行效果

C语言编译预处理详解_第1张图片

无参数的宏

其定义格式如下:

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

举个例子 

#include 
#define PI 3.141592

int main()
{
   printf("PI is %lf\n",PI);
}

运行效果 

执行预编译指令 gcc -E -o test100.E test100.c,得到 test100.E 文件

用vim打开test100.E文件,按 shift+g 跳到最后一行

C语言编译预处理详解_第2张图片

可以看到PI被替换成 3.141592 

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

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

带参数的宏

#define命令定义宏时,还可以为宏设置参数。与函数中的参数类似,在宏定义中的参数为形式参数,在宏调用中的参数称为实际参数。对带参数的宏,在调用中,不仅要宏展开,还要用实参去代换形参

带参宏定义的一般形式为:

#define 宏名(形参表) 字符串

在定义带参数的宏时,宏名和形参表之间不能有空格出现,否则,就将宏定义成为无参数形式,而导致程序出错

举个例子 

#include 
#define MAX(x,y) (x>y)?x:y

int main()
{

   printf("MAX is %d\n",MAX(3,5));
}

运行效果

C语言编译预处理详解_第3张图片

执行预编译指令 gcc -E -o test101.E test101.c,得到 test101.E 文件

C语言编译预处理详解_第4张图片

带参数的宏不容易理解,简单了解一下即可

四、条件编译

条件编译有多种格式,在这里只介绍最常用的两种格式 #ifdef#ifndef

#ifdef

#ifdef命令的使用格式如下:

#ifdef 标识符
  程序段 1
#else
  程序段 2
#endif
  1. 如果#ifdef后面的标识符已被定义过,则对 "程序段1" 进行编译;
  2. 如果没有定义标识符,则编译“程序段2”
  3. 一般不使用#else及后面的 "程序2"

举个例子 

#include 
#define Linux

int main()
{

    #ifdef Linux
    printf("这是Linux系统\n");

    #else
    printf("这不是Linux系统\n");

    #endif
}

运行效果 

执行预编译指令 gcc -E -o test102.E test102.c,得到 test102.E 文件

C语言编译预处理详解_第5张图片

#ifndef

而#ifndef的意义与#ifdef刚好相反,其格式如下:

#ifndef 标识符
  程序段 1
#else
  程序段 2 
#endif
  1. 如果未定义标识符,则编译“程序段1”;否则编译“程序段2”
  2. 一般用 #ifndef 来防止头文件被重复包含

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

第一条有效行的代码是

#ifndef _STDIO_H

接下来是

#define _STDIO_H  1

最后一行是。

#endif

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

你可能感兴趣的:(Re,:C语言,c语言,开发语言,linux)