拉里佩奇简介:预处理

来源:百度文库 编辑:中财网 时间:2024/04/30 06:39:55
预处理 2008-04-22 01:09 A.M.

预处理器指令主要有以下几种:
#define   定义一个宏替换
#undef    取消一个宏定义
#include   指定要包含的文件
#ifdef    测试某个宏已定义
#endif    表示#if的结束
#ifndef   测试某个宏未定义
#if       测试一个编译时条件
#else     当#if测试失败时,指定另一个测试

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

以下分别介绍:
一、宏替换指令
宏替换指令最常见的有三种:
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 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包含在双引号中时,首先从当前
目录中查找该文件,然后再到标准目录中查找。
#define "filename"
在这种情况下,只在标准目录中查找该文件。
也允许被包含文件的嵌套,也就是说,一个被包含的文件又可以包含其他文件,
但是,文件不能包含自身。
如果没有找到被包含的文件,将报告一个错误,且编译终止。
以下是一个.c文件的头文件:
#include
#include
#include
#include "_define.h"
#include "tc35605.h"
#include "port.h"
#include "bios2os.h"
#include "flash.h"
#include "lcddrv.h"
#include "uart.h"
#include "_vkey.h"
#include "bios.h"
#include "flashapi.h"
#include "diag.h"
#include "rfboottest.h"
#include "sysinfo.h"
#include "bios2os.h"
#include "cal.h"

三、编译器控制指令
当开发一个大型程序时,我们可能要面临以下一种或者多种情况。
(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_FILE
static 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 TEST
printf(....);
#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 标准还包括了两个新的预处理器操作:
#    字符串化运算符
##   标记符粘贴运算符

部分指令举例如下:
(a)、#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

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