首页 > 学院 > 开发设计 > 正文

C,预处理器指令

2019-11-06 06:52:37
字体:
来源:转载
供稿:网友

标准C预处理器(指令)

标准C预处理器

该文章借鉴一位老兄的文章,主要是取自PROGRAMMING IN ANSI c(Third Edition).

什么是预处理器?

答:预处理器是一个iechengxu,在源代码通过编译器之前,它先对源代码进行处理。它是在成为预处理器命令行或指令的控制下操作。预处理器指令放在源程序的main函数之前。在源代码通过编译器之前,由预处理器检查所有预处理指令。如果有预处理器指令,则采取相应的动作,然后再把源程序交给编译器。

 

 常用的预处理器指令集及其功能:

指令     作用#define    定义一个宏替换#undef     取消一个宏定义#include   指定要包含的文件#ifdef      测试某个宏已定义#endif      表示#if的结束#ifndef     测试某个宏未定义#if           测试一个编译时条件#else        当#if测试失败时,指定另一个测试

这些指令可以分为三类:(1)、宏替换指令(2)、文件包含指令(3)、编译器控制指令

以下分别介绍:一、宏替换指令宏替换是程序中的标识符被预定义的字符串(由一个或者多个标记符组成)取代的过程。预处理器在#define指令下完成这一工作。为宏定义中的表达式使用括号是明智之举。

指令最常见的有三种:1、简单宏替换#define PI              3.1415926

#define TITLE_HEIGTH 15#define EAR_MODE  0#define LOCAL_MODE  1#define STR_NOPIM       “Please check the PIM card.”#define START           main(){#define END             }#define BLANK_LINE      printf(“/n”)#define D               (66 + 88) 

2、含参数的宏含参数的替换成为宏调用(类似于函数调用)。当调用宏时,预处理器将替换该字符串,即用实参替换形参。字符串就像一个模板

#define CUBE(x)         ((x)*(x)*(x))

#define MAX(a,b)        (((a)>(b))?(a):(b))#define ABS(x)          (((x)>0)?(x):(-(x)))#define at_o_msg2(x) at_o_msg(x, x_strlen(x))#define _RGB2GRAY(r,g,b) ((((r) * 3) + ((g) * 6)+ ((b) * 1)) / 10)#define swap(a,b)       do { a ^= b; b ^= a; a ^= b; } while (0)

3、宏嵌套一种是:在一个宏的的定义中使用另一个宏。预处理器将扩展每个#define宏,知道文本中不再有宏为止。另一种是一个宏用作另一个宏的参数。如该段最后两个例子用嵌套调用来得出x,y和z三者的最大值。

#define M               6

#define N               M + 1#define SQUARE(x)       ((x)*(x))#define CUBE(x)         (SQUARE(x)*(x))#define SIXTH(x)        (CUBE(x)*SQUARE(x))

#define MAX(a,b)        (((a)>(b))?(a):(b))

#define MAXEX(x,y,z)    MAX(x,MAX(y,z))

二、文件包含指令两种形式:#include “filename”其中,filename为含有所需宏定义或函数的文件名。此时,预处理器把filename的整个内容插到程序的源代码之中。当filename包含在双引号中时,首先从当前目录中查找该文件,然后再到标准目录中查找。

#include  <filename>

在这种情况下,只在标准目录中查找该文件。

也允许被包含文件的嵌套,也就是说,一个被包含的文件又可以包含其他文件,但是,文件不能包含自身。如果没有找到被包含的文件,将报告一个错误,且编译终止。以下是一个.c文件的头文件:#include <stdio.h>

#include <string.h>#include <stdlib.h>#include “_define.h”#include “tc35605.h”#include “port.h”#include “diag.h”#include “rfboottest.h”#include “sysinfo.h”#include “bios2os.h”#include “cal.h”

Main()

{

       ……………

}

三、编译器控制指令当开发一个大型程序时,我们可能要面临以下一种或者多种情况。(1)、已包含的一个文件中含有某些宏定义,但不知道某个宏(假设TEST)是否定义在该头文件中。而我们想确认一下TEST是否已经定义。(2)、假设某个客户有两台不同类型的计算机,要求你编写一个可用在这两个系统上运行的程序。尽管针对每个系统的某些代码会不同,但仍想使用同一个程序。(3)、如果正在开发一个程序(假设用于销售分析),在公开市场上销售。某些客户可能坚持应有某些附加特性。而我们想用一个程序来满足两种客户的需求。(4)、假设正在测试我们的系统,这是一个规模较大的系统。我们可能希望在某些地方插入printf语句,用于显示中间结果和消息,以便跟踪运行流程和错误(如果有)。这里语句称为调试语句。我们可能想让这些语句成为程序的一部分,但只有当我们需要是才起作用。

    这些问题的一种解决办法是开发不同程序来不同情形的需求。另一种方法就是开发单个的全面的程序,它包含所有的可选代码,然后指定编译器跳过不需要的源代码。C 预处理器提供了一种称为条件编译的特性,它可用来关闭或打开程序的某一行或多行。

以下分别针对上面四种情况举例说明:1、情形1次情形指的是宏的条件定义。假设不管TEST宏是否已经在头文件中定义了,我们都想确保宏总是已经定义的。可以如下来实现,其中DEFINE.H为含有TEST宏定义的头文件。#define “DEFINE.H”

#ifndef TEST#define TEST 1#endif….语句 #ifndef在DEFINE.H文件中查找TEST的定义,如果没有定义,那么#ifndef与相应的#endif指令之间的代码将被执行。如果已经定义,相应的代码将被忽略。同样,不想让TEST定义,也是类似的定义。….#ifdef TEST#undef TEST#endif….

例如:

#ifdef _DEBUG

#define new DEBUG_NEW#undef THIS_FILEstatic char THIS_FILE[] = __FILE__;#endif

温馨提示:以上两种定义,不能直接定义成如下形式

#ifndef  TEST        // (It’s wrong!)

#undef   TEST        // (It’s wrong!)

2、情形2此时的main函数关心的是使程序可移植。这可以如下来实现:……main(){    …   #ifdef   IBM_PC   {     //the codes are for IBM_PC  … }

  #else   {      //the codes are for HP_PC   …. }

  #endif  ….}

如果我们想让程序在IBM_PC机上运行,可以在程序中包含如下指令:  #define IBM_PC否则就不需要。注意,编译器控制指令位于函数之中,还有注意把#字符放在该行的第一列中。如果定义了IBM_PC,编译器将编译针对IBM_PC的代码;如果没有,则编译针对HP_PC的代码。以下举个实例:switch(MonDriver.OnRunFunction) {  case DIALFUN:

   switch(byModemRet)   {    case MRES_CONN:     #ifdef DETTCPip      PostAppMessage(_iaPPTCPIP,EV_MCONNECT,1,0);      //connect OK,     #endif     MonDriver.RunStatus=ONLINE;     MonDriver.OnRunFunction=NOMDFUN;     break; 

    case MRES_TONE:     #ifdef DETTCPIP     //if no dial tone, send message to ppp      PostAppMessage(_iappTCPIP,EV_MCONNECT,4,0);           #endif     MonDriver.RunStatus=IDLE;     MonDriver.OnRunFunction=NOMDFUN;     break;

    case MRES_ERR:    case MRES_NO:    case MRES_BUSY:     #ifdef DETTCPIP      PostAppMessage(_iappTCPIP,EV_MCONNECT,2,0);      //the line is busy, send message to ppp     #endif     MonDriver.RunStatus=IDLE;     MonDriver.OnRunFunction=NOMDFUN;     break;

    default:     break;

   }

3、情形3这种情形类似于情形2,因此控制指令的形式如下:#ifdef  ABC    group-A lines

#else group-B lines#endif如果定义了ABC,则包含group-A 代码行;否则包含group-B 代码行。

例如:

#ifdef PIAFS_DATA_MODEM byBuf[0] = SID_APP_PHS;

#else byBuf[0] = 0xFF;#endif

4、情形4进行调试和测试是为了检测程序中的错误。编译器可以检测出语法和语义错误,但不能检测错误的算法,当程序运行时,将产生错误的结果。错误检测和隔离的过程首先是用已知的测试数据集对程序进行测试。程序分成几部分,在不同的地方放置printf语句以显示中间结果。这种语句称为调试语句。一旦把错误隔离并修正后就不再需要了。此时,我们可以把这些语句删掉,或使用如下控制指令来使它们不再为活动的。

  ……

#ifdef  TEST  {   printf(“Array elements/”);   for(i = 0; i < m; i ++)     printf(“x[%d] = %d/n”,i, x[i]); }#endif…..#ifdef TESTprintf(….);#endif只有定义了宏TEST,才包含位于#ifdef和#endif之间的语句。一旦所有事情都搞定了,就可以删除或者取消TEST的定义。这样可以使得#ifdef TEST条件为假,因而所有测试语句都被忽略。

C预处理器还支持一个更通用的测试条件形式,即#if指令,其形式如下:

#if  constant_expression {   statement-1;   statement-2;   …..  }

#endif

例如 1:

#if defined(PIAFS_DATA_MODEM) && !defined(_ONPC_)UChar g_PiafsStatus; extern void pf_set_dpdial_state(unsigned char state);extern void pf_set_dp_state(unsigned char state);

#endif //PIAFS_DATA_MODEM

例如 2:

#if   defined YAMAHA759 || /     defined YAMAHA762 ||  /     defined YAMAHA757 || /     defined OKI_2870  || /   defined SUNPLUS||/   defined WINBOND   Bios_SetRingVolume(0x1f);    MusicPlay(1);

#endif

例如 3:

#if PHONE_TEST extern _BYTE addSchedule(_BYTE *pBuf);//buf 128

#endif

constant_expression可以是以下任意形式的表达式:TEST <=3(LEVEL == 1||LEVEL == 2)MACHINE == ‘A’如果constant_expression为非零(即为真),那么位于#if 和 #endif之间的语句都被处理;否则被忽略。TEST、LEVEL等名称也可以定义为宏。

ANSI C 的其他预处理器指令#elif    提供另一种测试方法#pragma  指定某些指令#error   当发生错误时停止编译工作ANSI 标准还包括了两个新的预处理器操作:#    字符串化运算符##   标记符粘贴运算符

部分指令举例如下:

1、#elif指令

#elif指令用来构建“if…else…if”语句系列,用于测试多种条件情况。它的一般形式如下#if expression 1  statement sequence 1  #elif  expression 2   statement sequence 2    …. #elif  expression N   statement sequence N

#endif

例如:#ifdef __H300__

#define _CAL_FONT_BACK_COLOR __RGB(255,247,153)#define _CAL_PEN_COLOR   __RGB(192,180,2)#elif defined(__SS71C__) || defined(__SS72C__)#define _CAL_PEN_COLOR   __RGB(255,255,255)#define _CAL_FONT_BACK_COLOR __RGB(246,213,151)#endif

2、#pragma指令

#pragma是基于实现的指令,可以用来指定提交给编译器的不同命令。其形式如下:

#pragma name

其中,name为想要的pragma名,例如在microsoft c环境下:

#pragma loop_opt(on)  将会使循环优化后执行。如果编译器不能识别它,则被忽略。

3、#error指令#error指令用于在调试时产生诊断消息,其形式如下:#error error message当遇到#error指令时,显示错误消息并终止处理。例如:#ifndef FILE_G (或者这句这样说#if !define (FILE_G))

#error NO GRAPHICS FACILTY#endif

注意:这里与#if一起使用的是一个特殊的处理器运算符defined。Defined是一个新添加的指令,带有由括号括起来的name。如果编译器不支持它,可以进行如下替换:

用 #ifndef 替换 #if !defined

用 #ifdef 替换 #if defined

4、#字符串化运算符

#字符串化的运算符,用在宏函数的定义中。作用:该运算符允许在宏定义中使用一个形参,它将被转化为一个字符串。例如

#define sum(xy) printf(#xy“ = %f/n”, xy)

sum(a+b);

预处理器竟把下面语句行:sum(a+b)转换为printf(“a+b” “= %f /n”,a+b);

该语句又等价于 printf(“a + b = %f /n”,a + b);

ANSI标准还规定,相邻字符串将连接起来。

5、##标记符粘贴运算符

ANSI标准定义的标记符粘贴运算符##可以把宏定义中的两个标记符组合成一个标记符

例如 #define combine (s1, s2) s1##s2

Combine(TOTAL,SALES)则为 TOTALSALES

(结束)


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表