在嵌入式系统编程中不管是核心的驱动程式还是应用程式的编写,涉及到大量的预处理与条件编译,这样做的好处主要体现在代码的移植性强以及代码的修改方便等方面。引入了预处理与条件编译的概念。预处理是C语言的一个重要功能,它由预处理程式负责完成。当对一个源档案进行编译时,系统将自动引用预处理程式对源程式中的预处理部分作处理,处理完毕自动进入对源程式的编译。
基本介绍
- 中文名C语言预处理程式
- 外文名C language preprocessor
- 分类计算机
- 指令宏定义 档案包含 条件编译
- 控制指令
- 优势代码的移植性强,修改方便
- 套用嵌入式系统编程
简介
在嵌入式系统编程中不管是核心的驱动程式还是应用程式的编写,涉及到大量的预处理与条件编译,这样做的好处主要体现在代码的移植性强以及代码的修改方便等方面。引入了预处理与条件编译的概念。预处理(或称预编译)是指在进行编译的第一遍扫描(词法扫描和语法分析)之前所作的工作。预处理指令指示在程式正式编译前就由编译器进行的操作,可放在程式中任何位置。
预处理是C语言的一个重要功能,它由预处理程式负责完成。当对一个源档案进行编译时,系统将自动引用预处理程式对源程式中的预处理部分作处理,处理完毕自动进入对源程式的编译。C语言提供多种预处理功能,主要处理#开始的预编译指令,如宏定义(#define)、档案包含(#include)、条件编译(#ifdef)等。合理使用预处理功能编写的程式便于阅读、修改、移植和调试,也有利于模组化程式设计。
功能
在集成开发环境中,编译,连结是完成的。其实,C语言编译器在对原始码编译之前,还需要进一步的处理预编译。预编译的主要作用如下
1、将源档案中以”include”格式包含的档案複製到编译的源档案中。
2、用实际值替换用“#define”定义的字元串。
3、根据“#if”后面的条件决定需要编译的代码。
工作方式
预处理的行为是由指令控制的。这些指令是由#字元开头的一些命令。
#define指令定义了一个宏---用来代表其他东西的一个命令,通常是某一个类型的常量。预处理会通过将宏的名字和它的定义存储在一起来回响#define指令。当这个宏在后面的程式中使用到时,预处理器”扩展”了宏,将宏替换为它所定义的值。
#include指令告诉预处理器打开一个特定的档案,将它的内容作为正在编译的档案的一部分“包含”进来。例如下面这行命令
#include<stdio.h>指示预处理器打开一个名字为stdio.h的档案,并将它的内容加到当前的程式中。
预处理器的输入是一个C语言程式,程式可能包含指令。预处理器会执行这些指令,并在处理过程中删除这些指令。预处理器的输出是一个程式原程式的一个编辑后的版本,不再包含指令。预处理器的输出被直接交给编译器,编译器检查程式是否有错误,并经程式翻译为目标代码。
指令
类型
大多数预处理器指令属于下面3种类型
1、宏定义#define 指令定义一个宏,#undef 指令删除一个宏定义。
2、档案包含#include指令导致一个指定档案的内容被包含到程式中。
3、条件编译#if,#ifdef,#ifndef,,#elif,#else 和#dendif 指令可以根据编译器可以测试的条件来将一段文本包含到程式中或排除在程式之外。
剩下的#error,#line和#pragma指令更特殊的指令,较少用到。
规则
1、指令都是以#开始。#符号不需要在一行的行首,只要她之前有空白字元就行。在#后是指令名,接着是指令所需要的其他信息。
2、在指令的符号之间可以插入任意数量的空格或横向制表符。
3、指令总是第一个换行符处结束,除非明确地指明要继续。
4、指令可以出现在程式中德任何地方。我们通常将#define和#include指令放在档案的开始,其他指令则放在后面,甚至在函式定义的中间。
5、注释可以与指令放在同一行。
宏定义
无参数的宏
无参数的宏定义格式#define 宏名 字元串,各部分的含义如下
1、#表示这是一条预处理命令(凡是以“#”开始的均为预处理命令)。
2、define:关键字“define”为宏定义命令。
3、宏名是一个标示符,必须符合C语言标示符的规定,一般以大写字母标示宏名。
4、字元串可以是常数,表达式,格式串等。在前面使用的符号常量的定义就是一个无参数宏定义。
需要注意的是,预处理命令语句后面一般不会添加分号,如果在#define有分号,在宏替换时分号也将替换到原始码中去。在宏名和字元串之间可以有任意个空格。例如#define PI 3.14。
在使用宏定义时,还需要注意以下几点
1、宏定义是宏名来表示一个字元串,在宏展开时又以该字元串取代宏名。这只是一种简单的代换,字元串中可以含任何字元,可以是常数,也可以是表达式,预处理程式对它不作任何检查。如有错误,只能在编译已被宏展开后的源程式时发现。
2、宏定义必须写在函式之外,其作用域为宏定义命令起到源程式结束。
3、宏名在源程式只能够若用引号括起来,则预处理程式不对其作宏替换。
4、宏定义允许嵌套,在宏定义的字元串中可以使用已经定义的宏名。在宏展开时由预处理程式层层替换。
5、习惯上宏名可用大写字母表示,以方便与变数区别。但也允许用小写字母。
带参数的宏
#define命令定义宏时,还可以为宏设定参数。与函式中的参数类似,在宏定于中的参数为形式参数,在宏调用中的参数称为实际参数。对带参数的宏,在调用中,不仅要宏展开,还要用实参去代换形参。
带参宏定义的一般形式为#define 宏名(形参表) 字元串,在定义带参数的宏时,宏名和形参表之间不能有空格出现,否则,就将宏定义成为无参数形式,而导致程式出错。例如#define ABS(x) (x)<0?-(x):(x)。
以上的宏定义中,如果x的值小于0,则使用一元运算符(-)对其取负,得到正数。
带参的宏和带参的函式相似,但其本质是不同的。使用带参宏时,在预处理时将程式原始码替换到相应的位置,编译时得到完整的目标代码,而不进行函式调用,程式执行效率要高些。而函式调用只需要编译一次函式,代码量较少,一般情况下,对于简单的功能,可使用宏替换的形式来使用。
档案包含
当一个C语言程式由多个档案模组组成时,主模组中一般包含main函式和一些当前程式专用的函式。程式从main函式开始执行,在执行过程中,可调用当前档案中的函式,也可调用其他档案模组中的函式。如果在模组中要调用其他档案模组中的函式,必须在主模组中声明该函式原型。一般都是採用档案包含的方法,包含其他档案模组的头档案。档案包含中指定的档案名称即可以用引号括起来,也可以用尖括弧括起来,格式如下是#include< 档案名称>或#include“档案名称”如果使用尖括弧<>括起档案名称,则编译程式将到C语言开发环境中设定好的 include档案中去找指定的档案。因为C语言的标準头档案都存放在include资料夹中,所以一般对标準头档案採用尖括弧;对编程自己编写的档案,则使用双引号。如果自己编写的档案不是存放在当前工作资料夹,可以在#include命令后面加在路径。
#include命令的作用是把指定的档案模组内容插入到#include所在的位置,当程式编译连结时,系统会把所有#include指定的档案连结生成可执行代码。档案包含必须以#开头,表示这是编译预处理命令,行尾不能用分号结束。#include所包含的档案,其扩展名可以是“.c”,表示包含普通C语言源程式。也可以是 “.h”,表示C语言程式的头档案。C语言系统中大量的定义与声明是以头档案形式提供的。通过#define包含进来的档案模组中还可以再包含其他档案,这种用法称为嵌套包含。嵌套的层数与具体C语言系统有关,一般可以嵌套8层以上。
条件编译
预处理器还提供了条件编译功能。在预处理时,按照不同的条件去编译程式的不同部分,从而得到不同的目标代码。使用条件编译,可方便地处理程式的调试版本和正式版本,也可使用条件编译使程式的移植更方便。
使用#if
与C语言的条件分支语句类似,在预处理时,也可以使用分支,根据不同的情况编译不同的原始码段。
#if 的使用格式如下
#if 常量表达式
程式段
#else
程式段
#endif
该条件编译命令的执行过程为若常量表达式的值为真(非0),则对程式段1进行编译,否则对程式段2进行编译。可以使程式在不同条件下完成不同的功能。
#if 预编译命令还可使用多分支语句格式,具体格式如下
#if 常量表达式 1
程式段 1
#elif 常量表达式 2
程式段 2
… …
#elif 常量表达式 n
程式段 n
#else
程式段 m
#endif
使用#ifdef和#ifndef
在上面的#if 条件编译命令中,需要判断符号常量定义的具体值。在很多情况下,其实不需要判断符号常量的值,只需要判断是否定义了该符号常量。这时,可不使用#if命令,而使用一个预编译命令———#ifdef.
#ifdef命令的使用格式如下
#ifdef 标识符
程式段 1
#else
程式段 2
#endif
其意义是,如果#ifdef后面的标识符已被定义过,则对“程式段1”进行编译;如果没有定义标识符,则编译“程式段2”。一般不使用#else及后面的“程式2”。
而#ifndef的意义与#ifdef相反,其格式如下
#ifndef 标识符
程式段 1
#else
程式段 2
#endif
其意义是如果未定义标识符,则编译“程式段1”;否则编译“程式段2”。
使用#defined和#undef
与#ifdef 类似的,可以在#if命令中使用define来判断是否已定义指定的标识符。例如
#if defined 标识符
程式段 1
#endif
与下面的标示方式意义相同
#ifdef 标识符
程式段 1
#endif
也可使用逻辑运算符,对defined取反。例如
#if ! define 标识符
程式段 1
#endif
与下面的标示方式意义相同。
#ifndef 标识符
程式段 1
#endif
在#ifdef和#ifndef命令后面的标识符是使用#define进行定义的。在程式中,还可以使用#undef取消对标识符的定义,其形式为
#undef 标识符
例如
#define MAX 100
……
#undef MAX
在以上代码中,使用#define定义标识符MAX,经过一段程式代码后,又可以使用#undef取消已定义的标识符。使用#undef命令后,再使用#ifdef max,将不会编译后的原始码,因为此时标识符MAX已经被取消定义了。