深入浅出之预处理

预处理的输出是“翻译单元”,他是存放在内存中的临时文件,编译器接受预处理输出,并将源代码转换成包含机器语言指令的目标文件。

1. 宏替换


1) 字符串替换

格式: #define 宏名 宏体

其中宏名与宏体均为字符串。预处理时,将把程序中该宏定义之后的所有的宏名用宏体替换。

如:

#define PI 3.14

宏替换的好处:

(1) 提高程序的可读性

(2)易修改性好

2) 带参数宏定义格式

#define 标识符(参数表) 宏体

例如:

#define   PI   3.14
#define   RAD  2.0
#define   AREA return(PI*RAD*RAD)
带参数宏替换与函数区别:

(1) 时空效率不同,带参数宏替换效率比函数高

(2) 宏虽然可以带有参数,但宏替换过程中不像函数那样可以进行参数值的计算、传递及结果返回等操作;宏替换只是简单的字符替换,不进行计算。因此一些过程中不能用宏替换,比如递归调用;

 2. 书写#define命令应注意事项


1) 宏名与宏体之间应以空格相隔,所以宏名中不能含有空格

2) 宏名不能用引号括起来

比如#define “YES” 1  不进行宏替换

3) 较长的宏定义在一行中如果写不下时,可以在本行末尾使用反斜杠表示要续行

#define PRX printf(“ssss \

                                  ddddd”)

4)  对带参数宏定义,宏体及其各个形参应该用圆括号括起来

#include <QCoreApplication>
//#include "test.h"
#define f(x) x*x
#define g(x) (x)*(x)
#define h(x) ((x)*(x))
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
 
    qDebug("1:%d",f(2+2));
    qDebug("2:%d",g(2+2));
    qDebug("3:%d",h(2+2));
    return a.exec();
}
输出:
1:8 【2+2*2+2=8】
2:16【(2+2)*(2+2)=16】
3:16【((2*2)*(2*2))=16】

5) 宏定义不是C语言,不必再行末加分号

6)宏被定义后,一般不能再重新定义,而只能使用#undef命令终止该宏定义作用域(#undef 宏名) 

3. 文件包含


 格式1: #include “文件标识符”

按这种格式定义时,预处理程序首先在原来的源文件目录中检索该指定的文件;如果没有找到,则按系统指定的标准方式检索其他文件目录,直至找到为止;

格式2:#include <文件标识符>

按这种格式定义时,预处理程序只在系统库中寻找指定文件。

4. 条件编译


条件编译可使同一源程序在不同的编译条件得到不同的目标代码。

格式1:

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


格式2:

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


在工程的地方条件指示符#ifndef的最主要目的是防止头文件的重复包含和编译。

#ifndef _头文件名_H
#define _头文件名_H
 
....
#endif


格式3:

#if 表达式1
 
程序段1
 
#elif 表达式2
 
程序段2
 
#endif

5. #define宏定义的缺点

 1) 无法对宏定义中的变量进行类型检查

define定义的变量,是Compile-Time时期的变量,系统在编译时候,就将其全部替换,而不会对其变量进行类型等属性检查,相对不是很安全,可能存在潜在的问题,而没有发现。正因为其仅仅是编译时期替换,所以其定义的变量,是不会在运行时候分配内存的,不占用内存空间。

2) 未加括号带来的边界效应

由于宏定义的时候,其各个分量未加括号,而在使用宏定义的时候,传递的参数是变量的表达式,然后经过系统展开后,由于优先级的原因,导致其结果不是你所希望的。

#define MUL(A,B) A*B

而在使用的时候,这样的调用:

int a=1,b=2,c=3,d=0;

d=MUL(a+b,c)

经过编译时候展开,就变成了

d=a+b*c

而不是我们所希望的

d=(a+b)*c

[解决办法]

其解决办法也很简单,就是给每个分量,都加上括号,就可以避免此类问题

即,在宏定义的时候,如此定义:

#define MUL(A,B) ((A)*(B))

6. #define替代方案 

1) 用const代替define来定义常量

const TYPE ValueName = value;


2)用inline代替define来定义函数
define定义函数的最大缺点就是它只是简单替换,所以函数的参数可能会改变函数的行为。
比如上面定义的max,如果有这么一段代码:

int a = 5, b = 0;
max(++a, b);        // a is incremented twice
max(++a, b+10);      // a is incremented once


可以看到对两次调用,++a执行的次数是不一样的。而如果用inline,写成如下形式:

inline int max(int a, int b) { return a > b ? a : b; }

7.C++ 中的预定义宏 

  • 输出当前行的行号

预定义宏为:__LINE__,注意,是两个下划线。
使用方法为:cout << “Current Line Number __LINE__ : ” << __LINE__ << endl;

  • 输出当前文件名

预定义宏为:__FILE__
使用方法为:cout << “Current File Name __FILE__ : ” << __FILE__ << endl;

  • 输出当前文件编译日期

解释:形式为 month/day/year 的字符串,它表示把源文件转换为目标代码的日期。
预定义宏为:__DATE__
使用方法为:cout << “Current Date __DATE__ : ” << __DATE__ << endl;

  • 输出当前文件编译时间

解释:形式为 hour:minute:second 的字符串,它表示程序被编译的时间。
预定义宏为:__TIME__
使用方法为:cout << “Current Time__TIME__ : ” << __TIME__ << endl;
 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值