注:本课程参考文献《C安全编码标准》
欢迎关注我👆,收藏下次不迷路┗|`O′|┛ 嗷~~
目录
一.避免不安全宏参数的副作用
1.1背景
不安全的类函数宏在展开时,可能会导致其参数被多次计算或完全不被计算。因此,在调用这类宏时,应避免在参数中使用赋值、递增、递减、volatile访问、输入/输出操作或其他具有副作用(包括函数调用,因为函数调用也可能产生副作用)的表达式。
文档应当对不安全的宏在调用时若使用具有副作用的实参发出警告,但使用这些宏的责任最终落在程序员肩上。鉴于使用这类宏存在风险,建议尽量避免创建不安全的类函数宏。(用内联或者静态函数代替类函数的宏)
1.2案例
不安全宏的问题之一是宏实参的副作用。
#define ABS(x) (((x)<0)?-(x):(x))
void func(int n){
int m=ABS(++n);
}
例中的ABS()宏调用时扩展为
m=(((++n)<0)?-(++) : (++n));
形成的代码定义上没有问题,但是会导致n递增两次而不是一次。
-
#define ABS(x) (((x)<0)?-(x):(x))
这一行定义了一个宏
ABS
,用于计算一个数的绝对值。宏的参数是x
,通过三元运算符((x)<0)?-(x):(x)
来判断x
是否小于0。如果x
小于0,则ABS(x)
的值为-x
,即x
的相反数;如果x
不小于0,则ABS(x)
的值为x
本身。 -
void func(int n){
这一行定义了一个函数
func
,该函数接收一个整型参数n
,并且没有返回值(返回类型为void
)。 -
int m=ABS(++n);
这一行在函数
func
的体内声明了一个整型变量m
,并将其初始化为ABS(++n)
的结果。++n
是一个前缀递增操作,意味着在表达式被求值之前,n
的值会被增加1。然后,ABS(++n)
计算n
增加1后的绝对值,并将结果赋值给m
。
1.3安全修改方案
递增运算++n应在调用不安全宏之前执行。
#define ABS(x) (((x) < 0) ? -(x) : (x))
void func(int n){
++n;
int m = ABS(n);
}
而更为有效的办法是用内联或者静态函数代替类函数的宏指导方针,定义一个内联函数iabs()代替ABS()宏。ABS()宏可以在任何类型的操作数上运算,而iabs()函数与之不同它将截断宽度大于int类型,因为它的值不在后者范围内的参数。
#include <complex.h>
#include <math.h>
static inline int iabs(int x){
return (((x)<0)?-(x):(x));
}
void func(int n){
int m=iabs(++n);
}
1.4练习
考虑一个宏定义,用于计算两个数的最大值,但可能会因参数副作用导致错误的结果。
#define MAX(a, b) ((a) > (b) ? (a) : (b))
void func(int *ptr) {
int value = MAX(*ptr++, 10);
}
1.5答案
在这个例子中,如果ptr
指向的值小于10,则*ptr++
会被评估两次,导致ptr
递增两次,这是不期望的行为。
宏展开后的代码
宏MAX(*ptr++, 10)
展开为:
((*ptr++) > (10) ? (*ptr++) : (10))
如果*ptr
的初始值小于10,ptr
将递增两次。
安全修改方案
为了避免参数副作用,应在宏调用之前处理所有副作用。
#define MAX(a, b) ((a) > (b) ? (a) : (b))
void func(int *ptr) {
int current_value = *ptr;
ptr++;
int value = MAX(current_value, 10);
}
在这个修改后的版本中,ptr
的递增和值的获取是分开的,避免了宏参数中的副作用。
更进一步的优化
除了避免宏参数的副作用,另一个优化是尽量避免使用宏,特别是类函数宏,因为它们不提供类型检查,并且可能导致意外的副作用。可以考虑使用内联函数或模板(在C++中)来替代宏。
C语言内联函数示例:
static inline int max(int a, int b) {
return a > b ? a : b;
}
void func(int *ptr) {
int current_value = *ptr;
ptr++;
int value = max(current_value, 10);
}
C++模板示例:
template <typename T>
T max(T a, T b) {
return a > b ? a : b;
}
void func(int *ptr) {
int current_value = *ptr;
ptr++;
int value = max(current_value, 10);
}
这些替代方案提供了更好的类型安全性和更清晰的语义,同时避免了宏可能引入的问题。
非常感谢您花时间阅读我的博客,希望这些分享能为您带来启发和帮助。期待您的反馈与交流,让我们共同成长,再次感谢!
👇热门内容👇
Orbslam3&Vinsfusion_安城安的博客-CSDN博客
👇个人网站👇