深入浅出之预处理
Posted 浩瀚之水_csdn
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入浅出之预处理相关的知识,希望对你有一定的参考价值。
预处理的输出是“翻译单元”,他是存放在内存中的临时文件,编译器接受预处理输出,并将源代码转换成包含机器语言指令的目标文件。
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;
以上是关于深入浅出之预处理的主要内容,如果未能解决你的问题,请参考以下文章
Git之深入解析48个经典操作场景的分析和处理,专治不会合并代码