11、GNU C之宏定义中使用语句表达式

语句表达式的亮点在于定义复杂功能的宏。使用语句表达式来定义宏,不仅可以实现复杂的功能,而且还能避免宏定义带来的歧义和漏洞。
例:请定义一个宏,求两个数的最大值。
初级程序员写法1

#define  MAX(x, y)  x > y ? x : y
明显bug:变量不支持表达式

初级程序员写法2

#define MAX(x, y) (x) > (y) ? (x) : (y)

bug例程:

#include <stdio.h>
#define MAX(x, y) (x) > (y) ? (x) : (y)
int main(void)
{
    printf("max = %d\n", 3 + MAX(1, 2));
    return 0;
}
3 + (1) > (2) ? (1) : (2);

初级程序员写法3

#define MAX(x,y) ((x) > (y) ? (x) : (y))

bug例程

#include <stdio.h>
#define MAX(x, y) ((x) > (y) ? (x) : (y))
int main(void)
{
    int i = 2;
    int j = 6;

    printf("max = %d\n", MAX(i++, j++));

    return 0;
}

在程序中,我们定义两个变量 i 和 j,然后比较两个变量的大小,并作自增运算。实际运行结果发现 max = 7,而不是预期结果 max = 6。这是因为变量 i 和 j 在宏展开后,做了两次自增运算,导致打印出 i 的值为7。
中级程序员写法1

#include <stdio.h>

#define MAX(x, y) ({    \
    int _x = x;         \
    int _y = y;         \
    _x > _y ? _x : _y;  \
    })
int main(void)
{
    int i = 2;
    int j = 6;
    printf("max = %d\n", MAX(i++, j++));
    return 0;
}

在语句表达式中,我们定义了2个局部变量 _x、_y 来存储宏参数 x 和 y 的值,然后使用 _x 和 _y 来比较大小,这样就避免了 i 和 j 带来的2次自增运算问题。
高级程序员写法1
在上面这个宏中,我们定义的两个临时变量数据类型是 int 型,只能比较两个整型的数据。那对于其它类型的数据,就需要重新再定义一个宏了,这样太麻烦了!我们可以基于上面的宏继续修改,让它可以支持任意类型的数据比较大小:

#include <stdio.h>

#define MAX(type, x, y) ({    \
    type _x = x;        \
    type _y = y;         \
    _x > _y ? _x : _y;  \
    })


int main(void)
{
    int i = 2;
    int j = 6;
    printf("max = %d\n", MAX(int, i++, j++));
    printf("max = %lf\n", MAX(float, 3.33, 4.44));
    return 0;
}

在这个宏中,我们添加一个参数:type,用来指定临时变量 _x 和 _y 的类型。这样,我们在比较两个数的大小时,只要将2个数据的类型作为参数传给宏,就可以比较任意类型的数据了。
上面的宏定义中,我们增加了一个type类型参数,来兼容不同的数据类型,其实我们还有更加牛逼的语法,typeof是GNU C新增的一个关键字,用来获取数据类型,我们不用传参进去,让typeof直接获取!

#include <stdio.h>

#define MAX(x, y) ({    \
    typeof(x) _x = x;   \
    typeof(y) _y = y;   \
    (void)(&_x == &_y);  \
    _x > _y ? _x : _y;  \
    })


int main(void)
{
    int i = 2;
    int j = 6;


    printf("max = %d\n", MAX(i++, j++));
    printf("max = %lf\n", MAX(3.33, 4.44));

    return 0;
}

在这个宏定义中,使用了 typeof 关键字用来获取宏的两个参数类型。干货在**(void) (&x == &y);**这句话,简直是天才般的设计!一是用来给用户提示一个错误,对于不同类型的指针比较,编译器会给一个错误,提示两种数据类型不同;二是,当两个值比较,比较的结果没有用到,有些编译器可能会给出一个warning,加个(void)后,就可以消除这个警告!

Linux内核中使用语句表达式非常多。

#define min(x,y) ({ \
    typeof(x) _x = (x); \
    typeof(y) _y = (y); \
    (void) (&_x == &_y);    \
    _x < _y ? _x : _y; })

#define max(x,y) ({ \
    typeof(x) _x = (x); \
    typeof(y) _y = (y); \
    (void) (&_x == &_y);    \
    _x > _y ? _x : _y; })

#define min_t(type, a, b) min(((type) a), ((type) b))
#define max_t(type, a, b) max(((type) a), ((type) b))

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值