c/c++预处理过程详解(二)之条件编译及预定义的宏

来源:互联网 发布:制作身份证图片软件 编辑:程序博客网 时间:2024/05/17 04:49

未经博主同意不得私自转载!不准各种形式的粘贴复制本文及盗图!


首先对于上篇文章中宏定义的补充:

(1)#define NAME"zhangyuncong"

程序中有"NAME"则,它会不会被替换呢?

(2)#define 0x abcd

可以吗?也就是说,可不可以用不是标识符的字母替换成别的东西?

(3)#define NAME "zhang

这个可以吗?

(4)#define NAME"zhangyuncong"

程序中有上面的宏定义,并且,程序里有句:

NAMELIST这样,会不会被替换成"zhangyuncong"LIST

四个题答案都是十分明确的。

第一个,""内的东西不会被宏替换。这一点应该大都知道。

第二个,宏定义前面的那个必须是合法的用户标识符

第三个,宏定义也不是说后面东西随便写,不能把字符串的两个""拆开。

第四个:只替换标识符,不替换别的东西。NAMELIST整体是个标识符,而没有NAME标识符,所以不替换。

也就是说,这种情况下记住:#define第一位置第二位置

(1) 不替换程序中字符串里的东西。

(2) 第一位置只能是合法的标识符(可以是关键字)

(3) 第二位置如果有字符串,必须把""配对。

(4) 只替换与第一位置完全相同的标识符

Ps:上面这段来源自网路,主要是加强一下理解和实际应用中的问题。



条件编译:

使用#if  #else #elif  #endlf

#if的一般含义是如果#if后面的常量表达式为true,则编译它与#endif之间的代码,否则跳过这些代码。命令#endif标识一个#if块的结束。

注意:跟在#if后面的表达式在编译时求值,因此它必须仅含常量及已定义过的标识符,不可使用变量。表达式不许含有操作符sizeof(sizeof也是编译时求值)。

#else命令的功能有点象C语言中的else;#else建立另一选择(在#if失败的情况下)。

注意:#else属于#if块。

#elif命令意义与ELSE IF 相同,它形成一个if else-if阶梯状语句,可进行多种编译选择。#elif 后跟一个常量表达式。如果表达式为true,则编译其后的代码块,不对其它#elif表达式进行测试。否则,顺序测试下一块。

 #endif     结束一个#if……#else条件编译块

使用#ifdef和#ifndef

 

#ifdef表示“如果有定义”。有定义是指在编译此段代码时是否有某个宏通过 #define 指令定义的宏。如果在此之前已定义了这样的宏名,则编译语句段。

#ifndef表示“如果无定义”,指找不到通过#define定义的某宏,该宏可以是在当前文件此条指令的前面定义的,也可以是在其它文件中,但在此指令之前包含到该文件中的。如果没找到则不编译此语句段。

 

#ifdef的一般使用形式:

#ifdef 宏名 //语句段#endif


 

#ifndef同此

 

 

 

 C语言的#ifdef #if defined 的区别 

#ifdef 和 #if defined 的区别在于,后者可以组成复杂的预编译条件,比如

 

#if defined (AAA)&& defined (BBB)xxxxxxxxx#endif #if defined (AAA) ||VERSION > 12xxxxxxxxx#endif


 

而#ifdef 就不能用上面的用法,也就是说,当你要判断单个宏是否定义时

#ifdef 和 #if defined 效果是一样的,但是当你要判断复杂的条件时,只能用 #if defined

使用#defined和#undef

#define 定义一个宏定义。

使用形式:

#define MAX 100


#undef 取消其后那个前面已定义过有宏名定义。

使用形式:

#undef macroname


其他指令:

#error强迫编译程序停止编译,主要用于程序调试。

#error指令使预处理器发出一条错误消息,该消息包含指令中的文本.这条指令的目的就是在程序崩溃之前能够给出一定的信息。

#line        指令可以改变编译器用来指出警告和错误信息的文件号和行号。#pragma  指令没有正式的定义。编译器可以自定义其用途。典型的用法是禁止或允许某些烦人的警告信息。

#pragma

命令#pragma 为实现时定义的命令,它允许向编译程序传送各种指令。

#pragma的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。#pragma指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征。依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。

其格式一般为: #pragmapara

1message 参数。

 Message 参数能够在编译信息输出窗口中输出相应的信息,这对于源代码信息的控制是非常重要的。其使用方法为:

#pragma message(“消息文本”)

当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。

当我们在程序中定义了许多宏来控制源代码版本的时候,我们自己有可能都会忘记有没有正确的设置这些宏,此时我们可以用这条指令在编译的时候就进行检查。假设我们希望判断自己有没有在源代码的什么地方定义了_X86这个宏可以用下面的方法

#ifdef _X86#pragma message(“_X86 macro activated!”)#endif


当我们定义了_X86这个宏以后,应用程序在编译时就会在编译输出窗口里显示“_

X86 macro activated!”。我们就不会因为不记得自己定义的一些特定的宏而抓耳挠腮了。

 

2  code_seg 参数。

格式如:

#pragma code_seg(["section-name"[,"section-class"] ] )


它能够设置程序中函数代码存放的代码段,当我们开发驱动程序的时候就会使用到它。

 

3#pragma once (比较常用)

只要在头文件的最开始加入这条指令就能够保证头文件被编译一次。这条指令实际上在VC6中就已经有了,但是考虑到兼容性并没有太多的使用它。

 

4#pragma hdrstop

表示预编译头文件到此为止,后面的头文件不进行预编译。BCB可以预编译头文件以加快链接的速度,但如果所有头文件都进行预编译又可能占太多磁盘空间,所以使用这个选项排除一些头文件。

有时单元之间有依赖关系,比如单元A依赖单元B,所以单元B要先于单元A编译。你可以用#pragmastartup指定编译优先级,如果使用了#pragmapackage(smart_init) ,BCB就会根据优先级的大小先后编译。

 

5#pragma resource "*.dfm"

表示把*.dfm文件中的资源加入工程。*.dfm中包括窗体外观的定义。

 

6#pragma warning( disable : 4507 34; once : 4385; error : 164 )

等价于:

#pragma warning(disable:4507 34) /* 不显示4507和34号警告信息。如果编译时总是出现4507号警告和34号警告, 而认为肯定不会有错误,可以使用这条指令。*/#pragma warning(once:4385) // 4385号警告信息仅报告一次#pragma warning(error:164) // 把164号警告信息作为一个错误。同时这个pragmawarning 也支持如下格式:#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)。

 

7pragma comment(...)

该指令将一个注释记录放入一个对象文件或可执行文件中。

常用的lib关键字,可以帮我们连入一个库文件。

 

8progma pack(n)

指定结构体对齐方式。#pragmapack(n)来设定变量以n字节对齐方式。

n 字节对齐就是说变量存放的起始地址的偏移量有两种情况:

第一、如果n大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式,

第二、如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。

结构的总大小也有个约束条件,分下面两种情况:如果n大于所有成员变量类型所占用的字节数,那么结构的总大小必须为占用空间最大的变量占用的空间数的倍数;否则必须为n的倍数。

下面举例说明其用法。

#pragma pack(push) //保存对齐状态#pragma pack(4)//设定为4字节对齐struct test{char m1;double m4;int m3;};#pragma pack(pop)//恢复对齐状态


为测试该功能,可以使用sizeof()测试结构体的长度!

 

 

预定义的宏名
 
       ANSI C标准预定义了五个宏名,每个宏名的前后均有两个下画线,避免与程序员定义相同的宏名(一般都不会定义前后有两个下划线的宏)。这5个宏名如下:

1

2

3

4

5

__DATE__:当前源程序的创建日期。

 __FILE__:当前源程序的文件名称(包括盘符和路径)。

__LINE__:当前被编译代码的行号。

__STDC__:返回编译器是否位标准C,若其值为1表示符合标准C,否则不是标准C.

__TIME__:当前源程序的创建时间。  

 

例子:

#include<stdio.h>int main(){ int j;printf("日期:%s\n",__DATE__);printf("时间:%s\n",__TIME__};printf("文件名:%s\n",__FILE__);printf("这是第%d行代码\n",__LINE__);printf("本编译器%s标准C\n",(__STD__)?"符合":"不符合");   return 0;}


参考:

http://www.cnblogs.com/haore147/p/3646962.html

http://blog.chinaunix.net/uid-28458801-id-4424023.html


ps:一切内容均是本人根据网上各种途径,翻阅书籍等方式总结提炼的,如果设计版权希望能及时提醒更改。同时希望注重保护他人成果!



0 0