C安全编程教学-预处理器-避免不安全宏参数的副作用(一)_网络

注:本课程参考文献《C安全编码标准》

 欢迎关注我👆,收藏下次不迷路┗|`O′|┛ 嗷~~

目录

一.避免不安全宏参数的副作用

1.1背景

1.2案例

1.3安全修改方案

1.4练习

1.5答案

宏展开后的代码

安全修改方案

更进一步的优化


一.避免不安全宏参数的副作用

1.1背景

    不安全的类函数宏在展开时,可能会导致其参数被多次计算或完全不被计算。因此,在调用这类宏时,应避免在参数中使用赋值、递增、递减、volatile访问、输入/输出操作或其他具有副作用(包括函数调用,因为函数调用也可能产生副作用)的表达式。

    文档应当对不安全的宏在调用时若使用具有副作用的实参发出警告,但使用这些宏的责任最终落在程序员肩上。鉴于使用这类宏存在风险,建议尽量避免创建不安全的类函数宏。(用内联或者静态函数代替类函数的宏)

1.2案例

      不安全宏的问题之一是宏实参的副作用。

#define ABS(x) (((x)<0)?-(x):(x))
void func(int n){
    int m=ABS(++n);
}
  • 1.
  • 2.
  • 3.
  • 4.

    例中的ABS()宏调用时扩展为

m=(((++n)<0)?-(++) : (++n));

    形成的代码定义上没有问题,但是会导致n递增两次而不是一次。

  1. #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本身。
  2. void func(int n){这一行定义了一个函数func,该函数接收一个整型参数n,并且没有返回值(返回类型为void)。
  3. 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);
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

而更为有效的办法是用内联或者静态函数代替类函数的宏指导方针,定义一个内联函数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.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

1.4练习

    考虑一个宏定义,用于计算两个数的最大值,但可能会因参数副作用导致错误的结果。

#define MAX(a, b) ((a) > (b) ? (a) : (b))  
  
void func(int *ptr) {  
    int value = MAX(*ptr++, 10);  
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

1.5答案

    在这个例子中,如果ptr指向的值小于10,则*ptr++会被评估两次,导致ptr递增两次,这是不期望的行为。

宏展开后的代码

    宏MAX(*ptr++, 10)展开为:

((*ptr++) > (10) ? (*ptr++) : (10))
  • 1.

    如果*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);  
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

    在这个修改后的版本中,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);  
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

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);  
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

     这些替代方案提供了更好的类型安全性和更清晰的语义,同时避免了宏可能引入的问题。

 非常感谢您花时间阅读我的博客,希望这些分享能为您带来启发和帮助。期待您的反馈与交流,让我们共同成长,再次感谢!

👇个人网站👇

 安城安的云世界

 

C安全编程教学-预处理器-避免不安全宏参数的副作用(一)_安全_02