4.读c语言深度剖析 -- 宏定义(1)

 

1..数值宏常量

它可以出现在代码的任何地方,从本行宏定义开始,以后的代码就就都认识这个宏了;也可以把任何东西定义成宏。

因为编译器会在预编译的时候用真身替换替身,而在我们的代码里面却又用常常用替身来帮忙。看例子:   #define PI 3.141592654

const修饰的数据是有类型的,而define宏定义的数据没有类型。为了安全,我建议你以后在定义一些宏常数的时候用const代替,

编译器会给const修饰的只读变量做类型校验,减少错误的可能。但一定要注意const修饰的不是常量而是read only的变量

const修饰的只读变量不能用来作为定义数组的维数也不能放在case关键字后面

2.. 字符串宏常量

A),#define  ENG_PATH_1    E:\English\listen_to_this\listen_to_this_3

B),#define   ENG_PATH_2    “E:\English\listen_to_this\listen_to_this_3”

ENG_PATH_1加上双引号就成了:“ENG_PATH_1

3.. define宏定义注释符号可以吗

#define  BSC   //       

 #define  BMC  /*    

#define  EMC  */

D),BSC my single-linecomment

E),BMC my multi-linecomment  EMC

D)E)都错误,为什么呢?因为注释先于预处理指令被处理,当这两行被展开成//…或/**/,注释已处理完毕

此时再出现//…或/**/自然错误.因此,试图用宏开始或结束一段注释是不行的。(上机调试可知)

 4.. define宏定义表达式

求两个数的平方#define SQR(x)  x * x

假设x的值是个表达式10+1SQR(x)被替换后变成10+1*10+1这并不是想要得到的

怎么办括起来就好了  这样  #define SQR(x) ((x*x))

求两个数的和:#define SUM (x)x+x

而代码又写成这样:SUM (x)* SUM (x)。替换后变成:(5*3+5*3*5*3+5*3)。所以又错了!所以最外层的括号最好也别省了

要搞定宏定义表达式其实很简单,别吝啬括号就行了

注意这一点:宏函数被调用时是以实参代换形参。而不是“值传送”

5.. 宏定义中的空格

#define SUM   (x)(x+x)   SUM  后边有一个空格

这还是定义的宏函数SUMx)吗?显然不是。编译器认为这是定义了一个宏:SUM,其代表的是(x)(x+x)。

其关键原因还是在于SUM后面的这个空格.这个空格仅仅在定义的时候有效在使用这个宏函数的时候,空格会被编译器忽略掉

也就是说,定义好的宏函数SUMx)在使用的时候在SUM和(x)之间留有空格是没问题的。比如:SUM(3)SUM        3)的是一样的。

 6..#undef

#undef是用来撤销宏定义的,用法如下:

#define PI 3.141592654

// code

#undef PI

//下面的代码就不能用PI了,它已经被撤销了宏定义。

也就是说宏的生命周期从#define开始到#undef结束

#define X 3

#define Y X*2

#undef X

#define X 2

int z=Y;  

z = 4.  预处理的时候 X是等于2的  X等于3已经被撤销

7..#error预处理

#error预处理指令的作用是,编译程序时,只要遇到#error就会生成一个编译错误提

示消息,并停止编译。其语法格式为:#error   error-message:

#define ABC

 

#ifdef  ABC

#error C++ compiler required.  输出C++ compiler required.

#else

 

#endif

注意,宏串error-message不用双引号包围。遇到#error指令时,错误信息被显示,可能同时还显示编译程序作者预先定义的其他内容。

关于系统所支持的error-message信息,可以查找相关资料

8..#line预处理

#line的作用是改变当前行数和文件名称,它们是在编译程序中预先定义的标识符

命令的基本形式如下:

#line  number["filename"]

其中[]内的文件名可以省略。例如:

 #line 55 "a.h"

int main()    

{   

    printf("%d\n", __LINE__);                                 58

    printf("%s\n", __FILE__);                                  a.h

    printf("%s\n", __TIMESTAMP__);                   Fri Jun 24 10:29:00 2011

    return   0;   

}其中,文件名a.h可以省略不写。代表当前文件。。

这条指令可以改变当前的行号和文件名,例如上面的这条预处理指令就可以改变当前的行号

55,文件名是a.h。初看起来似乎没有什么用,不过,他还是有点用的,那就是用在编译器的编写中,

我们知道编译器对C源码编译过程中会产生一些中间文件,通过这条指令,

可以保证文件名是固定的,不会被这些中间文件代替,有利于进行分析。

9.. #运算符

#也是预处理

#define  SQR(x)   printf("Thesquareof x is%d.\n",((x)*(x)));

如果这样使用宏:

SQR(8);   则输出为:   The   squareof   x   is  64.

注意,引号中的字符x被当作普通文本来处理,而不被当作一个可以被替换的语言符号。

假如你确实希望在字符串中包含宏参数,那我们就可以使用“#”,它可以把语言符号转

化为字符串。上面的例子改一改:

#define  SQR(x)   printf("The  squareof   "#x"   is%d.\n",((x)*(x)));

再使用:SQR(8);  则输出的是:  The  squareof   8   is  64.

10..##预算符

#运算符一样,##运算符可以用于宏函数的替换部分。这个运算符把两个语言符号组

合成单个语言符号。看例子:    #define  XNAME(n)  x##n

如果这样使用宏:XNAME(8)   则会被展开成这样:  x8                   ##就是个粘合剂,将前后两部分粘合起来。

11.. 文件包含 include

文件包含是预处理的一个重要功能,它可用来把多个源文件连接成一个源文件进行编译,结果将生成一个目标文件。

C语言提供#include命令来实现文件包含的操作,它实际是 宏替换的延伸,有两种格式:

1  #include<filename>

其中,filename为要包含的文件名称,用尖括号括起来,也称为头文件,表示预处理到系统规定的路径中去获得这个文件

(即C编译系统所提供的并存放在指定的子目录下的头文件)。找到文件后,用文件内容替换该语句。

2  #include“filename”

其中,filename为要包含的文件名称。双引号表示预处理应在当前目录中查找文件名为ilename的文件,若没有找到,

则按系统指定的路径信息,搜索其他目录。找到文件后,用文件内容替换该语句。

需要强调的一点是:#include是将已存在文件的内容嵌入到当前文件中。

另外关于#include的路径也有点要说明:include支持相对路径,格式如trackant(蚁迹寻)所写:  .代表当前目录,..代表上层目录。


读c语言深度剖析 -- 宏定义(2)
2011-06-24 11:14

12..条件编译

条件编译的功能使得我们可以按不同的条件去编译不同的程序部分,因而产生不同的目

标代码文件。这对于程序的移植和调试是很有用的。条件编译有三种形式,下面分别介绍:

第一种形式:

#ifdef标识符 程序段1  #else  程序段2  #endif

它的功能是,如果标识符已被#define命令定义过则对程序段1进行编译;否则对程序段2

进行编译。如果没有程序段2(它为空),本格式中的#else可以没有,即可以写为:

#ifdef标识符 程序段  #endif

 

第二种形式:

#ifndef标识符 程序段1  #else程序段2  #endif

与第一种形式的区别是将“ifdef”改为“ifndef”。它的功能是,如果标识符#define命令定义过则对程序段1进行编译,否则对程序段2进行编译。这与第一种形式的功能正相反。

 

第三种形式:

#if常量表达式程序段1  #else  程序段2  #endif

它的功能是,如常量表达式的值为真(0),则对程序段1进行编译,否则对程序段2进行编译。因此可以使程序在不同条件下,完成不同的功能。

至于#elif命令意义与elseif相同,它形成一个if else-if阶梯状语句,可进行多种编译选择。

 

13..#pragma预处理

在所有的预处理指令中,#pragma指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。

#pragma指令对每个编译器给出了一个方法,在保持与CC++语言完全兼容的情况下,给出主机或操作系统专有的特征。

依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。

其格式一般为:    #pragma para       其中para为参数,下面来看一些常用的参数。

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

#pragma  message(“消息文本”)

假设我们希望判断自己有没有在源代码的什么地方定义了_X86这个宏可以用下面的方法

#ifdef  _X86

#pragma message (“_X86  macro   activated!”)

#endif

2.. #pragma  code_seg

另一个使用得比较多的pragma参数是code_seg。格式如:

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

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

3.. #pragma  once

只要在头文件的最开始加入这条指令就能够保证头文件被编译一次,这条指令实际上在

VisualC++6.0中就已经有了,但是考虑到兼容性并没有太多的使用它。

4.. #pragma  comment

#pragma  comment(...)  该指令将一个注释记录放入一个对象文件或可执行文件中。

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

#pragma comment(lib,"user32.lib")

该指令用来将user32.lib库文件加入到本工程中。

linker:将一个链接选项放入目标文件中,你可以使用这个指令来代替由命令行传入的或

者在开发环境中设置的链接选项,你可以指定/include选项来强制包含某个对象,例如:

#pragma comment (linker, "/include:__mySymbol")

5.. #pragma  hdrstop

#pragma hdrstop表示预编译头文件到此为止,后面的头文件不进行预编译。BCB可以

预编译头文件以加快链接的速度,但如果所有头文件都进行预编译又可能占太多磁盘空间,

所以使用这个选项排除一些头文件。

有时单元之间有依赖关系,比如单元A依赖单元B,所以单元B要先于单元A编译。

你可以用#pragma startup指定编译优先级,如果使用了#pragma package(smart_init)BCB

就会根据优先级的大小先后编译。

6..#pragma  resource

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

7.. #pragma warning 

8.. #pragma pack (研究内存对齐知识)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值