预处理指令是以#号开头的代码行。#号必须是该行除了任何空白字符外的第一个字符。#后是指令关键字,在关键字和#号之间允许存在任意个数的空白字符。整行语句构成了一条预处理指令,该指令将在编译器进行编译之前对源代码做某些转换。下面是部分预处理指令:
#if、#else和#endif指令
这些指令一般配合使用,为了避免那些只能包含一次的头文件被多次包含,可以在头文件中用编译时条件来进行控制。
#if defined(标识) //如果定义了标识
要执行的指令
#else
要执行的指令
#endif
在头文件中为了避免重复调用(比如说两个头文件互相包含对方),常采用这样的结构:
#if !(defined XXX) //XXX为一个在你的程序中唯一的标识符,
//每个头文件的标识符都不应相同。
//起标识符的常见方法是若头文件名为"abc.h"
//则标识为"abc_h"
#define XXX
真正的内容,如函数声明之类
#endif
#运算符
##运算符
条件编译指令
一:#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)
#pragmawarning(error: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)
struct A
{
char x;
inty;
}a;
sizeof(a)==5
(2)#pragmapack(2)
struct A
{
char x;
inty;
}a;
sizeof(a)==6
(3)#pragmapack(4)
struct A
{
char x;
inty;
}a;
sizeof(a)==8
(4)#pragmapack()
struct A
{
char x;
inty;
}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() {
}
五.#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
...
#elif 条件 n
#else
#endif
即可以设置不同的条件,在编译时编译不同的代码,预编译指令中的表达式与C语言本身的表达式基本一至如逻辑运算、算术运算、位运算等均可以在预编译指令中使用。之所以能够实现条件编译是因为预编译指令是在编译之前进行处理的,通过预编译进行宏替换、条件选择代码段,然后生成最后的待编译代码,最后进行编译。
#if 的一般含义是如果#if后面的常量表达式为true,则编译它所控制的代码,如条件1成立时就代码段1,条件1不成立再看条件2是否成立,如果条件2成立则编译代码段2,否则再依次类推判断其它条件,如果条件1-N都不成力则会编译最后的代码段n+1。
2、#ifdef, #else, #endif或#ifndef, #else, #endif
#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
{ ...
}
#endif
如果宏MY_PRINTF_EN定义为1则编译这段代码,如果宏定义不为1或者没有定义该宏,则不编译这段代码。同样也可以通过#ifdef或者#defined()实现,如
#define MY_PRINTF_EN 1
#if defined(MY_PRINTF_EN)
{ ...
}
#endif
在这种情况下两种方法具有异曲同工之妙,但试想如果你为了节约代码写了两个printf函数,在不同情况下使用不同的printf函数,一个是精简版一个是全功能标准版,如:
#define MY_PRINTF_SIMPLE
#ifdef MY_PRINTF_SIMPLE
{...
}
#endif
#ifdef MY_PRINTF_STANDARD
{...
}
#endif
同样可以用#if defined()实现
#define MY_PRINTF_SIMPLE
#if defined(MY_PRINTF_SIMPLE)
{...
}
#elif defined(MY_PRINTF_STANDARD)
{...
}
#endif
两种方法都可以实现,但可见后者更方便。但试想如果你有三个版本,用前者就更麻烦了,但方法相似,用后者就更方便,但仍需三个宏进行控制,你要住三个宏,改进一下就用#if可以用一个宏直接控制N种情况如:
#defineMY_PRINTF_VERSION
#if MY_PRINTF_VERSION == 1
{...
}
#elif MY_PRINTF_VERSION == 2
{...
}
#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
{ ...
}
#endif
但这样也会有一个问题,就是如果你忘了在config.h中添加宏MY_PRINTF_EN,那么自己写的printf函数也不会被编译,有些编译器会给出警告:MY_PRINTF_EN未定义。如果你有两个版本的想有一个默认版本,可以在printf.c中这样实现
#incldue "config.h"
#if !defined(MY_PRINTF_VERSION)
#endif
#if MY_PRINTF_VERSION == 1
{...
}
#elif MY_PRINTF_VERSION == 2
{...
}
#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
{...
}
#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版本了,要想兼容怎么办?那当然可以用更复杂的条件实现如:
#if MY_PRINTF_VERSION == 2 || MY_PRINTF_VERSION == 1
{...
}
#elif MY_PRINTF_VERSION == 3
int printf(unsigned char com_number, char* str)
{
}
#endif
不过还有另外一种方法,即使用#undef命令
#if MY_PRINTF_VERSION == 1
#endif
#if MY_PRINTF_VERSION == 2
{...
}
#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没有定义,但错就比较快。