预处理指令

来源:互联网 发布:java具体是做什么的 编辑:程序博客网 时间:2024/06/10 09:08

预处理指令是以#号开头的代码行。#号必须是该行除了任何空白字符外的第一个字符。#后是指令关键字,在关键字和#号之间允许存在任意个数的空白字符。整行语句构成了一条预处理指令,该指令将在编译器进行编译之前对源代码做某些转换。下面是部分预处理指令:

      指令            用途
 
                      空指令,无任何效果
 
       #include   包含一个源代码文件
 
       #define    定义宏
 
       #undef     取消已定义的宏
 
       #if            如果给定条件为真,则编译下面代码
 
       #ifdef       如果宏已经定义,则编译下面代码
 
       #ifndef    如果宏没有定义,则编译下面代码
 
       #elif         如果前面的#if给定条件不为真,当前条件为真,则编译下面代码
 
       #endif     结束一个#if……#else条件编译块
 
       #error     停止编译并显示错误信息



#if、#else和#endif指令

这些指令一般配合使用,为了避免那些只能包含一次的头文件被多次包含,可以在头文件中用编译时条件来进行控制。   

#if defined(标识) //如果定义了标识   

要执行的指令   

#else   

要执行的指令   

#endif   

在头文件中为了避免重复调用(比如说两个头文件互相包含对方),常采用这样的结构:  

#if !(defined XXX)  //XXX为一个在你的程序中唯一的标识符,   

//每个头文件的标识符都不应相同。   

//起标识符的常见方法是若头文件名为"abc.h"   

//则标识为"abc_h"   

#define XXX   

真正的内容,如函数声明之类   

#endif


#运算符
 

      出现在宏定义中的#运算符把跟在其后的参数转换成一个字符串。有时把这种用法的#称为字符串化运算符。例如:

 
          #define PASTE(n) "adhfkj"#n

 
          main()
 
          {
 
             printf("%s\n",PASTE(15));
 
          }
 
      宏定义中的#运算符告诉预处理程序,把源代码中任何传递给该宏的参数转换成一个字符串。所以输出应该是adhfkj15。

##运算符
 
      ##运算符用于把参数连接到一起。预处理程序把出现在##两侧的参数合并成一个符号。看下面的例子:

 
          #define NUM(a,b,c) a##b##c
 
          #define STR(a,b,c) a##b##c

 
          main()
 
          {
 
              printf("%d\n",NUM(1,2,3));
 
              printf("%s\n",STR("aa","bb","cc"));
 
          }

 
      最后程序的输出为:
 
               123
 
               aabbcc
 
      千万别担心,除非需要或者宏的用法恰好和手头的工作相关,否则很少有程序员会知道##运算符。绝大多数程序员从来没用过它。


条件编译指令
 

  条件编译指令将决定那些代码被编译,而哪些是不被编译的。可以根据表达式的值或者某个特定的宏是否被定义来确定编译条件。
 
  1.#if指令
 
      #if指令检测跟在制造另关键字后的常量表达式。如果表达式为真,则编译后面的代码,知道出现#else、#elif或#endif为止;否则就不编译。
 
  2.#endif指令
 
      #endif用于终止#if预处理指令。

 
          #define DEBUG 0
 
          main()
 
          {
 
              #if DEBUG
 
                  printf("Debugging\n");
 
              #endif
 
                  printf("Running\n");
 
          }

 
      由于程序定义DEBUG宏代表0,所以#if条件为假,不编译后面的代码直到#endif,所以程序直接输出Running。
 
      如果去掉#define语句,效果是一样的。
 
  3.#ifdef和#ifndef
 
      #define DEBUG

 
      main()
 
      {
 
          #ifdef DEBUG
 
              printf("yes\n");
 
          #endif
 
          #ifndef DEBUG
 
              printf("no\n");
 
          #endif
 
      }
 
      #if defined等价于#ifdef; #if !defined等价于#ifndef
 
  4.#else指令
 
      #else指令用于某个#if指令之后,当前面的#if指令的条件不为真时,就编译#else后面的代码。#endif指令将中指上面的条件块。

 
      #define DEBUG

 
      main()
 
      {
 
          #ifdef DEBUG
 
              printf("Debugging\n");
 
          #else
 
              printf("Not debugging\n");
 
          #endif
 
              printf("Running\n");
 
     }

 
  5.#elif指令
 
      #elif预处理指令综合了#else和#if指令的作用。

 
      #define TWO

 
      main()
 
      {
 
          #ifdef ONE
 
              printf("1\n");
 
          #elif defined TWO
 
              printf("2\n");
 
          #else
 
              printf("3\n");
 
          #endif
 
      }
 
      程序很好理解,最后输出结果是2。

 
  6.其他一些标准指令
 
      #error指令将使编译器显示一条错误信息,然后停止编译。
 
      #line指令可以改变编译器用来指出警告和错误信息的文件号和行号。
 
      #pragma指令没有正式的定义。编译器可以自定义其用途。典型的用法是禁止或允许某些烦人的警告信息。

一:#pragma warning指令

该指令允许有选择性的修改编译器的警告消息的行为
指令格式如下:
#pragma warning( warning-specifier : warning-number-list [;warning-specifier : warning-number-list...]
#pragma warning( push[ ,n ] )
#pragma warning( pop )

主要用到的警告表示有如下几个:

once:只显示一次(警告/错误等)消息
default:重置编译器的警告行为到默认状态
1,2,3,4:四个警告级别
disable:禁止指定的警告信息
error:将指定的警告信息作为错误报告

如果大家对上面的解释不是很理解,可以参考一下下面的例子及说明

#pragma warning( disable : 4507 34; once : 4385; error : 164)
等价于:
#pragma warning(disable:4507 34) // 不显示4507和34号警告信息
#pragmawarning(once:4385) 
      // 4385号警告信息仅报告一次
#pragmawarning(error:164) 
      //  把164号警告信息作为一个错误。
同时这个pragma warning 也支持如下格式:
#pragma warning( push [ ,n ] )
#pragma warning( pop )
这里n代表一个警告等级(1---4)。
#pragma warning( push )保存所有警告信息的现有的警告状态。
#pragma warning( push, n)保存所有警告信息的现有的警告状态,并且把全局警告
等级设定为n。 

#pragma warning( pop )向栈中弹出最后一个警告信息,在入栈和出栈之间所作的
一切改动取消。例如:
#pragma warning( push )
#pragma warning( disable : 4705 )
#pragma warning( disable : 4706 )
#pragma warning( disable : 4707 )
#pragma warning( pop )

在这段代码的最后,重新保存所有的警告信息(包括4705,4706和4707)

在使用标准C++进行编程的时候经常会得到很多的警告信息,而这些警告信息都是不必要的提示,
所以我们可以使用#pragma warning(disable:4786)来禁止该类型的警告

在vc中使用ADO的时候也会得到不必要的警告信息,这个时候我们可以通过
#pragma warning(disable:4146)来消除该类型的警告信息

二:#pragma pack()
注:如果设置的值比结构体中字节最长的类型还要大,则这个变量(注意仅针对这一个变量)只按照它的字节长度对齐,即不会出现内存浪费的情况。请参见(4)。

(1)#pragmapack(1)       //每个变量按照1字节对齐
struct A
{
char x; 
  //aligned on byte boundary 0
inty; 
   //aligned on byte boundary 1
}a;
sizeof(a)==5

(2)#pragmapack(2)       //每个变量按照2字节对齐
struct A
{
char x; 
  //aligned on byte boundary 0
inty; 
   //aligned on byte boundary 2
}a;
sizeof(a)==6

(3)#pragmapack(4)       //每个变量按照4字节对齐
struct A
{
char x; 
  //aligned on byte boundary 0
inty; 
   //aligned on byte boundary 4
}a;
sizeof(a)==8

(4)#pragmapack()       //默认,相当于#pragma pack(8) 每个变量按照8字节对齐
struct A
{
char x; 
  //aligned on byte boundary 0
inty; 
   //aligned on byte boundary 4
}a;
sizeof(a)==8
但是这里y的大小是4字节,所以不会按照8字节对齐,否则将造成1个int空间的浪费

三.#pragma comment
The following pragma causes the linker to search for the EMAPI.LIBlibrary while linking. The linker searches first in the currentworking directory and then in the path specified in the LIBenvironment variable:
#pragma comment( lib, "emapi" )


四.#pragma deprecated
When the compiler encounters a deprecated symbol, it issuesC4995:
void func1(void) {}
void func2(void) {}
int main() {
 
  func1();
 
  func2();
 
  #pragma deprecated(func1,func2)
 
 func1();   // C4995
 
 func2();   // C4995
}


五.#pragma message
The following code fragment uses the message pragma to display amessage during compilation:
#if _M_IX86 == 500
#pragma message( "Pentium processor build" )
#endif


有些程序在调试、兼容性、平台移植等情况下可能想要通过简单地设置一些参数就生成一个不同的软件,这当然可以通过变量设置,把所有可能用到的代码都写进去,在初始化时配置,但在不同的情况下可能只用到一部分代码,就没必要把所有的代码都写进去,就可以用条件编译,通过预编译指令设置编译条件,在不同的需要时编译不同的代码。


(一)条件编译方法
条件编译是通过预编译指令来实现的,主要方法有:
1、#if, #elif, #else, #endif
#if 条件 1
 代码段 1
#elif 条件 2
 

  代码段 2
...
#elif 条件 n
 
代码段 n
#else
 
代码段 n+1
#endif
即可以设置不同的条件,在编译时编译不同的代码,预编译指令中的表达式与C语言本身的表达式基本一至如逻辑运算、算术运算、位运算等均可以在预编译指令中使用。之所以能够实现条件编译是因为预编译指令是在编译之前进行处理的,通过预编译进行宏替换、条件选择代码段,然后生成最后的待编译代码,最后进行编译。

#if 的一般含义是如果#if后面的常量表达式为true,则编译它所控制的代码,如条件1成立时就代码段1,条件1不成立再看条件2是否成立,如果条件2成立则编译代码段2,否则再依次类推判断其它条件,如果条件1-N都不成力则会编译最后的代码段n+1。

2、#ifdef, #else, #endif或#ifndef, #else, #endif
 
  条件编译的另一种方法是用#ifdef与#ifndef命令,它们分别表示“如果有定义”及“如果无定义”。有定义是指在编译此段代码时是否有某个宏通过#define 指令定义的宏,#ifndef指令指找不到通过 #define定义的某宏,该宏可以是在当前文件此条指令的关面定义的,也可以是在其它文件中,但在此指令之前包含到该文件中的。
#ifdef的一般形式是:
#ifdef macro_name
代码段 1
#else
代码段 2
#endif

#ifndef的一般形式是:
#ifndef macro_name
代码段 2
#else
代码段 1
#endif
这两段代码的效果是完全一样的。
3、通过宏函数defined(macro_name)
参数为宏名(无需加""),如果该macro_name定义过则返回真,否则返回假,用该函数则可以写比较复杂的条件编译指令如
#if defined(macro1) || (!defined(macro2) && defined(macro3))...#else...#endif(二)条件编译技巧与示例(1)#ifdef和#defined()比较  首先比较一下这两种方法,第一种方法只能判断一个宏,如果条件比较复杂实现起来比较烦锁,用后者就比较方便。如有两个宏MACRO_1,MACRO_2只有两个宏都定义过才会编译代码段A,分别实现如下:#ifdef MACRO_1#ifdef MACRO_2 代码段 A#endif#endif
或者
#if defined(MACRO_1) &&defined(MACRO_2)
 
代码段  A

#endif
同样,要实现更复杂的条件用#ifdef更麻烦,所以推荐使用后者,因为即使当前代码用的是简单的条件编译,
以后在维护、升级时可能会增加,用后者可维护性较强。旧的编译器可能没有实现#defined()指令,
C99已经加为标准。要兼容老的编译器,还需用#ifdef指令。
2、#if与 #ifdef或#if defined()比较
  比如自己写了一个printf函数,想通过一个宏MY_PRINTF_EN实现条件编译,用#if可实现如下
#define MY_PRINTF_EN 1
#if MYS_PRINTF_EN == 1
 
int printf(char* fmt, char* args, ...)
{ ...
}
#endif
如果宏MY_PRINTF_EN定义为1则编译这段代码,如果宏定义不为1或者没有定义该宏,则不编译这段代码。同样也可以通过#ifdef或者#defined()实现,如
#define MY_PRINTF_EN 1
#if defined(MY_PRINTF_EN)
 int printf(char* fmt,char* args, ...)
{ ...
}
#endif
在这种情况下两种方法具有异曲同工之妙,但试想如果你为了节约代码写了两个printf函数,在不同情况下使用不同的printf函数,一个是精简版一个是全功能标准版,如:
#define MY_PRINTF_SIMPLE
 

#ifdef MY_PRINTF_SIMPLE
 
  void printf(*str) //向终端简单地输出一个字符串
{...
}
#endif
#ifdef MY_PRINTF_STANDARD
 
int printf(char* fmt, char* args, ...)
{...

#endif
同样可以用#if defined()实现
#define MY_PRINTF_SIMPLE
 

#if defined(MY_PRINTF_SIMPLE)
 
  void printf(*str) //向终端简单地输出一个字符串
{...
}
#elif defined(MY_PRINTF_STANDARD)
 
int printf(char* fmt, char* args, ...)
{...

#endif
两种方法都可以实现,但可见后者更方便。但试想如果你有三个版本,用前者就更麻烦了,但方法相似,用后者就更方便,但仍需三个宏进行控制,你要住三个宏,改进一下就用#if可以用一个宏直接控制N种情况如:
#defineMY_PRINTF_VERSION 
   1
 

#if MY_PRINTF_VERSION == 1
 
  void printf(*str) //向终端简单地输出一个字符串
{...
}
#elif MY_PRINTF_VERSION == 2
 int printf(char* fmt,char* args, ...)
{...

#elif MY_PRINTF_VERSION == 3
int printf(unsigned char com_number, char* str)
{
}
#else
默认版本
#endif
这样,你只需修改一下数字就可以完成版本的选择了
看来好像用#if比较好了,试想如下情况:你写了一个配置文件叫做config.h用来配置一些宏,通过这些宏来控制代码,如你在config.h的宏
#define MY_PRINTF_EN 1
来控制是否需要编译自己的printf函数,
而在你的源代码文件printf.c中有如下指令
#i nclude "config.h"
#if MY_PRINTF_EN == 1
 
int printf(char* fmt, char* args, ...)
{ ...
}
#endif
但这样也会有一个问题,就是如果你忘了在config.h中添加宏MY_PRINTF_EN,那么自己写的printf函数也不会被编译,有些编译器会给出警告:MY_PRINTF_EN未定义。如果你有两个版本的想有一个默认版本,可以在printf.c中这样实现
#incldue "config.h"
#if !defined(MY_PRINTF_VERSION)
 
#defineMY_PRINTF_VERSION                
#endif

#if MY_PRINTF_VERSION == 1
 
  void printf(*str) //向终端简单地输出一个字符串
{...
}
#elif MY_PRINTF_VERSION == 2
 int printf(char* fmt,char* args, ...)
{...

#elif MY_PRINTF_VERSION == 3
int printf(unsigned char com_number, char* str)
{
}
#endif
这种情况下还得用到#ifdef或#if defined(),你可以不用动主体的任何代码,只需要修改printf.c文件中MY_RPINTF_VERSION宏的数字就可以改变了,如果用前面那种方法还得拖动代码,在拖动中就有可能造成错误。
再试想,如果软件升级了,或者有了大的改动,原来有三个版本,现在只剩下两个版本了,如
#if MY_PRINTF_VERSION == 2
int printf(char* fmt, char* args, ...)
{...

#elif MY_PRINTF_VERSION == 3
int printf(unsigned char com_number, char* str)
{
}
#endif
因为这些核心代码不想让使用这些代码的人关心,他们只需要修改config.h文件,那就要在printf.c中实现兼容性。如果以前有人在config.h配置宏MY_PRINTF_VERSION为1,即有
#define MY_PRINTF_VERSION 
 1
而现在没有1版本了,要想兼容怎么办?那当然可以用更复杂的条件实现如:
#if MY_PRINTF_VERSION == 2 || MY_PRINTF_VERSION == 1
 int printf(char* fmt,char* args, ...)
{...

#elif MY_PRINTF_VERSION == 3
int printf(unsigned char com_number, char* str)
{
}
#endif
不过还有另外一种方法,即使用#undef命令
#if MY_PRINTF_VERSION == 1
 
#undef MY_PRINTF_VERSION
 
#define MY_PRINTF_VERSION 2
#endif
#if MY_PRINTF_VERSION == 2
 int printf(char* fmt,char* args, ...)
{...

#elif MY_PRINTF_VERSION == 3
int printf(unsigned char com_number, char* str)
{
}
#endif

用#if还有一个好处,如果你把宏名记错了,把MY_PRINTF_EN定义成了MY_PRINT_EN,那么你用#ifdefMY_PRINTF_EN或者#if defined(MY_PRINTF_EN)控制的代码就不能被编译,查起来又不好查,
用#if  MY_PRINTF_EN==1控制就很好查,因为你把MY_PRINTF_EN定义成MY_PRINT_EN,则MY_PRINTF_EN、实际上没有定义,那么编译器会给出警告#ifMY_PRINTF_EN == 1中的MY_PRINTF_EN没有定义,但错就比较快。
 
from: http://blog.csdn.net/tykgls/article/details/6695315
0 0
原创粉丝点击