编译器各类宏使用总结

转:express

C/C++中宏总结C程序的源代码中可包括各类编译指令,这些指令称为预处理命令。虽然它们实际上不是C语言的一部分,但却扩展了C程序设计的环境。本节将介绍如何应用预处理程序和注释简化程序开发过程,并提升程序的可读性。ANSI标准定义的C语言预处理程序包括下列命令:ide

 

#define#error,#include#if#else#elif#endif#ifdef#ifndef#undef#line#pragma等。很是明显,全部预处理命令均以符号#开头,下面分别加以介绍。函数

 

命令#define定义了一个标识符及一个串。在源程序中每次遇到该标识符时,均以定义的串代换它。ANSI标准将标识符定义为宏名,将替换过程称为宏替换。命令的通常形式为:测试

 

#define identifier stringthis

 

注意:spa

? 该语句没有分号。在标识符和串之间能够有任意个空格,串一旦开始,仅由一新行结束。操作系统

? 宏名定义后,便可成为其它宏名定义中的一部分。命令行

? 宏替换仅仅是以文本串代替宏标识符,前提是宏标识符必须独立的识别出来,不然不进行替换。例如:翻译

#define XYZ this is a tes设计

使用宏printf("XYZ")//该段不打印"this is a test"而打印"XYZ"。由于预编译器识别出的是"XYZ"

? 若是串长于一行,能够在该行末尾用一反斜杠' \'续行。

 

处理器命令#error强迫编译程序中止编译,主要用于程序调试。

 

#include 命令#i nclude使编译程序将另外一源文件嵌入带有#i nclude的源文件,被读入的源文件必须用双引号或尖括号括起来。例如:

 

i nclude"stdio.h"或者#i nclude

 

这两行代码均使用C编译程序读入并编译用于处理磁盘文件库的子程序。

将文件嵌入#i nclude命令中的文件内是可行的,这种方式称为嵌套的嵌入文件,嵌套层次依赖于具体实现。

 

若是显式路径名为文件标识符的一部分,则仅在哪些子目录中搜索被嵌入文件。不然,若是文件名用双引号括起来,则首先检索当前工做目录。若是未发现文件,则在命令行中说明的全部目录中搜索。若是仍未发现文件,则搜索实现时定义的标准目录。

 

若是没有显式路径名且文件名被尖括号括起来,则首先在编译命令行中的目录内检索

 

若是文件没找到,则检索标准目录,不检索当前工做目录。

 

条件编译命令

有几个命令可对程序源代码的各部分有选择地进行编译,该过程称为条件编译。商业软件公司普遍应用条件编译来提供和维护某一程序的许多顾客版本。

 

#if#else#elif#endif

 

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

 

#if constant-expression

statement sequence

#endif

 

跟在#if后面的表达式在编译时求值,所以它必须仅含常量及已定义过的标识符,不可以使用变量。表达式不准含有操做符sizeofsizeof也是编译时求值)。

 

#else命令的功能有点象C语言中的else#else创建另外一选择(在#if失败的状况下)。注意,# else属于# if块。

 

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

 

#if expression

statement sequence

#elif expression1

statement sequence

#endif

 

在嵌套的条件编译中#endif#else#elif与最近#if#elif匹配。

 

# ifdef # ifndef

 

条件编译的另外一种方法是用#ifdef#ifndef命令,它们分别表示"若是有定义""若是无定义"# ifdef的通常形式是:

 

# ifdef macroname

statement sequence

#endif

 

#ifdef#ifndef能够用于#if#else#elif语句中,但必须与一个#endif

 

命令#undef 取消其后那个前面已定义过有宏名定义。通常形式为:

#undef macroname

 

命令# line改变__LINE____FILE__的内容,它们是在编译程序中预先定义的标识符。命令的基本形式以下:

 

# line number["filename"]

 

其中的数字为任何正整数,可选的文件名为任意有效文件标识符。行号为源程序中当前行号,文件名为源文件的名字。命令# line主要用于调试及其它特殊应用。注意:在#line后面的数字标识从下一行开始的数字标识。

 

预约义的宏名

 

ANSI标准说明了C中的五个预约义的宏名。它们是:

 

__LINE__

__FILE__

__DATE__

__TIME__

__STDC__

 

若是编译不是标准的,则可能仅支持以上宏名中的几个,或根本不支持。记住编译程序也许还提供其它预约义的宏名。

 

__LINE____FILE__宏指令在有关# line的部分中已讨论,这里讨论其他的宏名。

__DATE__宏指令含有形式为月//年的串,表示源文件被翻译到代码时的日期。

源代码翻译到目标代码的时间做为串包含在__TIME__中。串形式为时:分:秒。

若是实现是标准的,则宏__STDC__含有十进制常量1。若是它含有任何其它数,则实现是非标准的。编译C++程序时,编译器自动定义了一个预处理名字__cplusplus,而编译标准C时,自动定义名字__STDC__

 

 

2、#line和#error
#line用于重置由__LINE__和__FILE__宏指定的行号和文件名。
用法以下:#line number filename
例如:#line 1000 //将当前行号设置为1000
     #line 1000 "lukas.c"   //行号设置为1000,文件名设置为lukas.c

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

3、#pragma
在全部的预处理指令中,#Pragma 指令多是最复杂的了。#pragma的做用是设定编译器的状态或者是指示编译器完成一些特定的动做。#pragma指令对每一个编译器给出了一个方法,在保持与C和C++语言彻底兼容的状况下,给出主机或操做系统专有的特征。依据定义,编译指示是机器或操做系统专有的,且对于每一个编译器都是不一样的。
其格式通常为: #Pragma Para
其中Para 为参数,下面来看一些经常使用的参数。

(1)message 参数。 Message 参数是我最喜欢的一个参数,它可以在编译信息输出窗口中输出相应的信息,这对于源代码信息的控制是很是重要的。其使用方法为:
#Pragma message(“消息文本”)
当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。
当咱们在程序中定义了许多宏来控制源代码版本的时候,咱们本身有可能都会忘记有没有正确的设置这些宏,此时咱们能够用这条指令在编译的时候就进行检查。假设咱们但愿判断本身有没有在源代码的什么地方定义了_X86这个宏能够用下面的方法
#ifdef _X86
#Pragma message(“_X86 macro activated!”)
#endif
当咱们定义了_X86这个宏之后,应用程序在编译时就会在编译输出窗口里显示“_
X86 macro activated!”。咱们就不会由于不记得本身定义的一些特定的宏而抓耳挠腮了。

(2)另外一个使用得比较多的pragma参数是code_seg。格式如:
#pragma code_seg( ["section-name"[,"section-class"] ] )
它可以设置程序中函数代码存放的代码段,当咱们开发驱动程序的时候就会使用到它。

(3)#pragma once (比较经常使用)
只要在头文件的最开始加入这条指令就可以保证头文件被编译一次。这条指令实际上在VC6中就已经有了,可是考虑到兼容性并无太多的使用它。

(4)#pragma hdrstop表示预编译头文件到此为止,后面的头文件不进行预编译。BCB能够预编译头文件以加快连接的速度,但若是全部头文件都进行预编译又可能占太多磁盘空间,因此使用这个选项排除一些头文件。
有时单元之间有依赖关系,好比单元A依赖单元B,因此单元B要先于单元A编译。你能够用#pragma startup指定编译优先级,若是使用了#pragma package(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号警告信息做为一个错误。
同时这个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)。

(7)pragma comment(...)
该指令将一个注释记录放入一个对象文件或可执行文件中。
经常使用的lib关键字,能够帮咱们连入一个库文件。 

(8)progma pack(n)
   
 指定结构体对齐方式!#pragma pack(n)来设定变量以n字节对齐方式n 字节对齐就是说变量存放的起始地址的偏移量有两种状况:第1、若是n大于等于该变量所占用的字节数,那么偏移量必须知足默认的对齐方式,第2、若是n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用知足默认的对齐方式。结构的总大小也有个约束条件,分下面两种状况:若是n大于全部成员变量类型所占用的字节数,那么结构的总大小必须为占用空间最大的变量占用的空间数的倍数; 不然必须为n的倍数。下面举例说明其用法。

#pragma pack(push) //保存对齐状态
#pragma pack(4)//设定为4字节对齐
struct test
{
char m1;
double m4;
int m3;
};
#pragma pack(pop)//恢复对齐状态
为测试该功能,可使用sizeof()测试结构体的长度!

 

 

 

 

 

在你写dll的时候,由于对于C和C++,编译器会有不一样的名字解析规则,因此能够这样用

#ifndef   __STDC__

extern "C "   void   function();

#else

void   function();

#endif

 

 __LINE__           在源代码中插入当前源代码行号
  __FILE__           在源代码中插入当前源代码文件名
  __DATE__           在源代码中插入当前编译日期〔注意和当前系统日期区别开来〕
  __TIME__           在源代码中插入当前编译时间〔注意和当前系统时间区别开来〕  
  __STDC__           当要求程序严格遵循ANSIC标准时该标识符被赋值为1。
----------------------------------------------------------------------------

标识符__LINE__和__FILE__一般用来调试程序;标识符__DATE__和__TIME__一般用来在编译后的程序中加入一个时间标志,以区分程序的不一样版本;当要求程序严格遵循ANSIC标准时,标识符__STDC__就会被赋值为1;当用C++编译程序编译时,标识符__cplusplus就会被定义。

#include

 

int main ()

{

    printf("该输出行在源程序中的位置:%d\n", __LINE__ );

    printf("该程序的文件名为:%s\n", __FILE__ );

    printf("当前日期为:%s\n", __DATE__ );

    printf("当前时间为:%s\n", __TIME__ );

 

    return 0;

}

相关文章
相关标签/搜索