浅谈宏定义的弊端

浅谈宏定义的弊端

前言

代码规范告诉我们,代码中不应出现魔数,于是我们用大量的宏去定义常量。宏定义作为一种预处理方式,提供了一种方便的替换功能。但是如果使用不当,却会导致一些非意料中的结果!

因此使用宏定义时,我们应当了解其可能带来的一些陷阱,如类型安全、边际效应等问题。一旦出现问题,这些错误往往是隐晦的,难以排查。

1. 宏定义的作用域

在源文件定义时,其作用域为定义之后到源文件结束,所以通常会写在文件开头,以扩大其作用域。

在头文件中定义时,作用域是从包含该头文件的位置到文件结尾,同时 #undef 可终止宏定义的作用域

**但是要注意的是,宏定义的作用域不受命名空间或者说 { } 的限制。**如下面这个例子:

namespace abc
{
	define PATH_MAX	256;
}

char path[PATH_MAX];

以上代码可以通过编译,但这不是我们想要的,可以说这是C++的一个糟糕设计吧。

2. 优点

可大量替换代码中的字面常量,利于维护。

如果是用宏定义替代一些简单函数,则可以提高效率,因为函数要保存现场,有参数传递和返回

3. 陷阱

所谓的边际效应,就是因为简单替换而引起的,如

//优先级问题示例1
#define N 10
#define	M 100+25

此时计算 M*N 的结果会是 (100 + 25 * 10),而我们期待的结果是(100 + 25) * 10
                
//优先级问题示例2
#define DIV(x,y) (x+y-1)/y
那么 
	a = DIV( b & c, 5);
将被转化为:
	a = ( b & c + 5 - 1) / 5; 
                
而 +/- 运算符优先级高于 & ,那么上面式子等同于:
	a = ( b & (c + 5 - 1) ) / 5; 
                
这不是我们要的结果,为保证优先级正确,所有替换要加括号()                
//定义伪函数时,参数不是传递的
#dedfine sqrt(a) ((a)*(a))

int main()
{
    int a = 10;
    int r = 0;
    
    r = sqrt(a++);
    prntf("%d,%d", a, r);
}
输出结果: 12,100
因为a++被执行了两次, (a++)* (a++)
    
所以这限制了我们的写法,只能写为:
    a++; r = sqrt(a);

当然还有更多其它形式的边际效应问题,所以使用宏定义一些复杂表达式 或者伪函数时,一定要慎重

3.1 一个宏定义重名导致的错误

看下面的一个案例,两个头文件中定义了两个重名的宏

//filename1.h
#define PATH_MAX (256)

//filename2.h
#define PATH_MAX (1024)

//xxxx.cpp
#include "system.h"
struct node
{
	char path[PATH_MAX];
    ...
    int a;
};

这里假定filename1.h 中的宏是我们自己定义的,我们只想使用filename1.h中的定义。 而filename2.h是别人定义的或一些系统库定义的。

正常情况下,如果一个同时包含了这两个头文件, 会出现重定义编译错误,那么只包含其中一个头文件的话是可以正常编译的。

当我们忘记包含了filename1.h,而恰好因为系统头文件 “system.h” 的包含层级很深,在我们不知道的情况下将 filename2.h包含了进来那么代码正常编译,但代码运行时却会出现很隐晦的错误并难以排查,如越界等。

那么使用const定义能解决这种问题么?不能!但是const 定义的变量是可以有命名空间的,我们可以使用命名空间避免这种人为错误:

namespace my_namespace
{
	const int pATH_MAX = 256;
}

char path[my_namespace::pATH_MAX];

4. 建议

综上,定义字面常量时,建议使用const 代替 #define

const int PATH_MAX = 256;

使用const 定义的优点有:

拥有类型,可帮助编译器侦测出错误用法,并解决边际效应(因为它是个变量而不是简单替换)

如果定义的常量是一个表达式的结果,const 定义只执行一次表达式,而#define定义则会执行多次,从这个角度讲用const定义效率更高。

可以在命名空间内定义,从而避免重名导致的错误

posted @ 2020-08-23 13:59  HL棣  阅读(29)  评论(0编辑  收藏  举报  来源