宏(使用注意事项、主要用途)------c++程序设计原理与实践(进阶篇)
使用宏的时候一定要小心:在c中没有真正有效的方法来避免使用宏,但宏带有严重的副作用,因为宏不遵守通常的c(或c++)作用域和类型规则——它只是一种文本替换。
宏的使用注意事项:
- 所以宏名全部大写。
- 不是宏的结构不要使用全部大写的名字。
- 不要为宏取短的或“有趣”的名字,如max或min。
- 期望其他人也遵守上面简单而常见的规范。
宏的主要用途:
- 定义“常量”。
- 定义类似函数的结构。
- “改进”语法。
- 控制条件编译。
- 其他不常见用途。
我们认为宏被过度使用了,但在c++程序中有时很难避免使用宏(特别是当编写的程序需要移植到很老的编译器上或者有特殊限制的平台上,在c程序中还没有一种合理而完整的替代方法。)。
下面介绍一些有关的宏技术。
类函数宏:
下面是一个非常典型的类函数宏:
#define MAX(x,y)((x)>=(y)?(x):(y))
我们为宏取名为全大写字母的MAX,以便与常用的函数名max区别开来,显然,它与函数还是有很大区别的:没有参数类型、没有语句块、没有返回值语句等。另外,宏定义中的那些括号是起什么作用呢?考虑如下代码:
int aa=MAX(1,2);
double dd=MAX(aa++,2);
char cc=MAX(dd,aa)+2;
宏替换后,程序扩展为:
int aa=((1)>=(2)?(1):(2));
double dd=((aa++)>=(2)?(aa)++:(2));
char cc=((dd)>=(aa)?(dd):(aa))+2;
在宏的定义中,使用任何参数时都应将其置于括号之中(当作表达式)。如果在宏定义中没有使用“那些括号”。最后一条语句会扩展为:
char cc=dd>=aa?dd:aa+2;
也就是说,cc的值将和你根据其定义推断出的值不同。
另一方面,对于第二条语句,使用再多括号耶解决不了问题。宏参数x被替换为aa++,由于x在MAX使用了两次,因此x进行了两次增1运算。注意,不要向宏传递可能引起副作用的参数。
如何取消定义宏:
#undef MAX(x,y);
并不是所以宏参数都被用作表达式。例如:
#define ALLOC(T,n) ((T*)malloc(sizeof(T)*n)); //malloc 为c中的函数 类似c++的new
这是来自实际程序中的例子,内存分配时sizeof中使用的类型与所需类型可能不匹配,这个宏对避免此类错误很有用:
double* p= malloc(sizeof(int)*10); //可能错误
如果还希望宏能捕获内存耗尽的错误:假如已经定义了error_var和error(),可以这样定义宏:
#define ALLOC(T,n) (errpr_var=(T*)malloc(sizeof(T)*n),\
(error_var==0)\
?(error("memory allocation failure"),0)\
:error_var)
行结尾出的 \ 并非输入错误,将一个较长的宏分成几行,就必须在非结尾行的最后加上 \ 。
语法宏:
你可以定义这样一类宏,它们能使源程序形式上更符合你的偏好,例如:
#define forever for(;;)
#define CASE break;case
#define begin {
#define end }
但不建议使用。
条件编译:
假设某个头文件有两个版本,比如说一个是Linux版,另一个是Windows版。在程序中你如何选择使用哪个版本呢?常用方法如下:
#ifdef WINDOWS
#lnclude"my_windows_header.h"
#else
#include"my_linux_header.h"
#endif
现在,如果有人在编译之前定义了宏 WINDOWS,则效果为:
#lnclude"my_windows_header.h"
否则,效果为:
#include"my_linux_header.h"
#indef WINDOWS 并不关心 WINDOWS被定义成什么,它只关心WINDOWS是否被定义。
很多大型系统(包括所有操作系统)都会定义类似WINDOWS这样的宏,以供我们检验。
我们还可以这样来检查程序是在被c++编译器编译还是被c编译器编译:
#ifdef __cplusplus
//in c++
#else
//in c
#endif
还有一种类似的结构,通常被人们称为包含保护,常常用来防止头文件被保护多次:
#ifndef MY_WINDOWS_HEADER
#define MY_WINDOWS_HEADER
//下面是头文件内容
#endif
#ifndef检测宏是否未被定义,即它与#ifdef是相对的。逻辑上,用于源文件控制的宏与其他修改源码(宏替换)的宏有很大不同。他们只是使用了相同的下层语言机制。
c++程序设计原理与实践(进阶篇)
宏(使用注意事项、主要用途)------c++程序设计原理与实践(进阶篇)的更多相关文章
- 数值限制------c++程序设计原理与实践(进阶篇)
每种c++的实现都在<limits>.<climits>.<limits.h>和<float.h>中指明了内置类型的属性,因此程序员可以利用这些属性来检 ...
- 编码原则实例------c++程序设计原理与实践(进阶篇)
编码原则: 一般原则 预处理原则 命名和布局原则 类原则 函数和表达式原则 硬实时原则 关键系统原则 (硬实时原则.关键系统原则仅用于硬实时和关键系统程序设计) (严格原则都用一个大写字母R及其编号标 ...
- 函数形参为基类数组,实参为继承类数组,下存在的问题------c++程序设计原理与实践(进阶篇)
示例: #include<iostream> using namespace std; class A { public: int a; int b; A(int aa=1, int bb ...
- 函数返回值string与返回值bool区别------c++程序设计原理与实践(进阶篇)
为什么find_from_addr()和find_subject()如此不同?比如,find_from_addr()返回bool值,而find_subject()返回string.原因在于我们想说明: ...
- (c++11)随机数------c++程序设计原理与实践(进阶篇)
随机数既是一个实用工具,也是一个数学问题,它高度复杂,这与它在现实世界中的重要性是相匹配的.在此我们只讨论随机数哦最基本的内容,这些内容可用于简单的测试和仿真.在<random>中,标准库 ...
- 实现求解线性方程(矩阵、高斯消去法)------c++程序设计原理与实践(进阶篇)
步骤: 其中A是一个n*n的系数方阵 向量x和b分别是未知数和常量向量: 这个系统可能有0个.1个或者无穷多个解,这取决于系数矩阵A和向量b.求解线性系统的方法有很多,这里使用一种经典的方法——高斯消 ...
- 有符号数和无符号数------c++程序设计原理与实践(进阶篇)
有符号数与无符号数的程序设计原则: 当需要表示数值时,使用有符号数(如 int). 当需要表示位集合时,使用无符号数(如unsigned int). 有符号数和无符号数混合运算有可能会带来灾难性的后果 ...
- bitest(位集合)------c++程序设计原理与实践(进阶篇)
标准库模板类bitset是在<bitset>中定义的,它用于描述和处理二进制位集合.每个bitset的大小是固定的,在创建时指定: bitset<4> flags; bitse ...
- gets()scanf()有害------c++程序设计原理与实践(进阶篇)
最简单的读取字符串的方式是使用gets(),例如: char a[12]; gets(a); 但gets()和scanf()是有害的,曾经有大约1/4的成功黑客攻击是由于gets()和它的近亲scan ...
随机推荐
- Python web框架 flask
Flask是一个基于Python开发并且依赖jinja2模板和Werkzeug WSGI服务的一个微型框架,对于Werkzeug本质是Socket服务端,其用于接收http请求并对请求进行预处理,然后 ...
- CORS跨域资源共享漏洞
CORS漏洞其中已经存在很久了,但是国内了解的人不是很多,文章更是少只有少,漏洞平台也没有此分类. 在DefConChina之后写了一篇算是小科普的文章. 定义CORS,Cross-Origin Re ...
- python利用scapy模块写一个TCP路由追踪和扫描存活IP的脚本
前言: 没有前言 0x01 from scapy.all import * import sys from socket import * import os from threading impor ...
- Python3 持久化pickle模块
pickle提供了一个简单的持久化功能.可以将对象以文件的形式存放在磁盘上. 1.pickle.dump(obj, file[, protocol]) 序列化对象,并将结果数据流写入到文件对象中.参数 ...
- blockground
颜色的表示方法 一共有三种:单词.rgb表示法.十六进制表示法 rgb:红色 绿色 蓝色 三原色光学显示器,每个像素都是由三原色的发光原件组成的,靠明亮度不同调成不同的颜色的.用逗号隔开,r.g.b的 ...
- [原创]Spring JdbcTemplate 使用总结与经验分享
引言 近期开发的几个项目,均是基于Spring boot框架的web后端项目,使用JdbcTemplate执行数据库操作,实际开发过程中,掌握了一些有效的开发经验,踩过一些坑,在此做个记录及总结,与各 ...
- 无法安装 Microsoft Visual Studio 2010 Service Pack 1
解决办法: 32 位系统删除:HKEY_LOCAL_MACHINE\Software\Microsoft\VSTO Designtime Setup\ 64 位系统删除:HKEY_LOCAL_MACH ...
- 有一些sql 是必须要做笔记的!!
select CONCAT(unix_timestamp(),"-",id,"-",name) as aa,age from workers; //连接字段 s ...
- Java中包、类、方法、属性、常量的命名规则
1:包(package):用于将完成不同功能的类分门别类,放在不同的目录(包)下,包的命名规则:将公司域名反转作为包名.比如www.baidu.com 对于包名:每个字母都需要小写.比如:com.ba ...
- 4-3 线程安全性-原子性-synchronized
原子性它提供了互斥访问,同一时刻只能有一个线程来对它进行操作.能保证同一时刻只有一个线程来对其进行操作的,除了Atomic包之外,还有锁.JDK提供锁主要分两种,synchronized是一个Java ...