宏比较值,坑的一B

嵌入式Linux

共 4989字,需浏览 10分钟

 ·

2020-12-07 21:33

昨晚上,我准备睡觉,连总给我发了一段代码

#include "stdio.h"

#define MAX_MACRO(a, b) ((a) > (b) ? (a) : (b))
int MAX_FUNC(int a, int b) {
 return ((a) > (b) ? (a) : (b));
}

int main()
{
 unsigned int a = 1;
 int b = -1;
 printf("MACRO: max of a and b is: %d\n", MAX_MACRO(++a,b));
 printf("FUNC : max of a and b is: %d\n", MAX_FUNC(a, b));
 return 0;
}

刚开始我是懵得一逼,我觉得应该有坑,就没有回答,连总也说了,这个问题肯定有坑,原话是「没有坑我就不发给你了」,既然有坑,我就没有继续看下去,我怕掉下去后起不来,觉都没得睡了。

然后今天我继续搞了下这个代码

执行结果如下

MACRO: max of a and b is: -1
FUNC : max of a and b is: 2

--------------------------------
Process exited after 0.03527 seconds with return value 0
请按任意键继续. . .

我百思不得姐,遂上网查了下,得到的结果如下

http://www.myexception.cn/c/324054.html

------解决方案-------------------- 

因为a是unsigned,比较时b会转成unsigned,(unsigned)-1是最大的 

------解决方案--------------------

1.在包含两种数据类型的任何运算里,两个值都被转换成两种类型里面的较高级别。

2.类型级别从高到低的顺序是long double, double, float, unsigned long long, long long, long, unsigned int 和 int.

基于这样的解释,我把上面的代码修改成这样

#include "stdio.h"

#define MAX_MACRO(a, b) ((a) > (b) ? (a) : (b))
int MAX_FUNC(int a, int b) {
 return ((a) > (b) ? (a) : (b));
}

int main()
{
 unsigned int a = 1;
 long b = -1;
  
 printf("MACRO: max of a and b is: %d\n", MAX_MACRO(++a,b));
 printf("FUNC : max of a and b is: %d\n", MAX_FUNC(a, b));
 return 0;
}

我把 int 修改成了 long,因为longunsigned int的前面,如果上面的解释正确,那么应该输出的是1

实际输出如下:

MACRO: max of a and b is: -1
FUNC : max of a and b is: 2

--------------------------------
Process exited after 0.03618 seconds with return value 0
请按任意键继续. . .

所以上面的解释不正确

针对这个问题,我们在群里讨论了很久,得出了结论只能是 这是未定义行为

未定义行为 就是编译器想怎么就怎么搞,但是写这个代码的人,肯定也是有问题的,因为这个代码是可以优化的。

还有一种情况,我们把代码修改成下面这个样子

#include "stdio.h"

#define MAX_MACRO(a, b) ((a) > (b) ? (a) : (b))
int MAX_FUNC(int a, int b) {
 return ((a) > (b) ? (a) : (b));
}

int main()
{
 unsigned char a = 1;
 long b = -1;
  
 printf("MACRO: max of a and b is: %d\n", MAX_MACRO(++a,b));
 printf("FUNC : max of a and b is: %d\n", MAX_FUNC(a, b));
 return 0;
}

这时候代码的输出,大家可以先思考一下

思考后,,,,,,,,,

代码输出如下

MACRO: max of a and b is: 3
FUNC : max of a and b is: 3

--------------------------------
Process exited after 0.03109 seconds with return value 0
请按任意键继续. . .

原因是,宏替换后,++a 被替换了3次

MAX_MACRO(++a,b)

((++a) > (b) ? (++a) : (b))

上面扯了那么多,主要是想说两个事情

1、宏获取最大值的代码,这么写容易出现问题。

2、知道了问题,我们就需要知道如何写是正确的,用函数的方式就是一种正确的方式

当然,不仅是我们的代码是这样写的,我看到内核代码里面也这样写

weiqifa@bsp-ubuntu1804:~$ head -n 2 /usr/include/sys/param.h
/* Compatibility header for old-style Unix parameters and limits.
   Copyright (C) 1995-2018 Free Software Foundation, Inc.
weiqifa@bsp-ubuntu1804:~$ egrep 'MIN\(|MAX\(' /usr/include/sys/param.h 
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
weiqifa@bsp-ubuntu1804:~$ uname -sr
Linux 4.15.0-117-generic
weiqifa@bsp-ubuntu1804:~$ 

既然这个宏不安全,还这样写出来,只能说有一种可能,写的人希望用这个函数的人比较专业

— — 网上也给出了一个比较专业的写法

 #define max(a,b) \
   ({ __typeof__ (a) _a = (a); \
       __typeof__ (b) _b = (b); \
     _a > _b ? _a : _b; })

__typedef__ 这个关键字也有点鸡肋,懂的人比较少,之前分析一个内核函数的时候解释过。

—— 测试代码

#include "stdio.h"

int main(void)
{
 int a = 100;
 __typeof__(a) b = -20;
 printf("%d %d\n",sizeof(b),b);
 return (0);
}

程序输出

4 -20

--------------------------------
Process exited after 0.03424 seconds with return value 0
请按任意键继续. . .

好,既然我们知道了 typedef 这个好东西,我们再修改一个测试程序

#include "stdio.h"

#define MAX_MACRO(a, b) ((a) > (b) ? (a) : (b))
int MAX_FUNC(int a, int b) {
 return ((a) > (b) ? (a) : (b));
}
#define max(a,b) \
({ __typeof__ (a) _a = (a); \
   __typeof__ (b) _b = (b); \
 _a > _b ? _a : _b; })

int main()
{
 unsigned char a = 1;
 long b = -1;
  
 printf("MACRO: max of a and b is: %d\n", max(++a,b));
 printf("FUNC : max of a and b is: %d\n", MAX_FUNC(a, b));
 return 0;
}

这个代码也是有坑的,不小心的人可能又掉坑里了。

程序输出

MACRO: max of a and b is: 2
FUNC : max of a and b is: 2

--------------------------------
Process exited after 0.03569 seconds with return value 0
请按任意键继续. . .

__typeof__ 修改成 typeof 再试一次

#include "stdio.h"

#define MAX_MACRO(a, b) ((a) > (b) ? (a) : (b))
int MAX_FUNC(int a, int b) {
 return ((a) > (b) ? (a) : (b));
}
#define max(a,b) \
({ typeof (a) _a = (a); \
   typeof (b) _b = (b); \
 _a > _b ? _a : _b; })

int main()
{
 unsigned char a = 1;
 long b = -1;
  
 printf("MACRO: max of a and b is: %d\n", max(++a,b));
 printf("FUNC : max of a and b is: %d\n", MAX_FUNC(a, b));
 return 0;
}

程序输出

MACRO: max of a and b is: 2
FUNC : max of a and b is: 2

--------------------------------
Process exited after 0.02998 seconds with return value 0
请按任意键继续. . .

可以看到,结果和上面的一样

— — 再看一个修改方案

除了上面的修改方案外,还有另外一种修改方案

代码如下

#include 


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

#define ENSURE_int(i)   _Generic((i), int:   (i))
#define ENSURE_float(f) _Generic((f), float: (f))


#define MAX(type, x, y) \
  (type)GENERIC_MAX(ENSURE_##type(x), ENSURE_##type(y))
  
int main()
{
    int a = 3;
    int b = 5;
    
    printf("%d\n",MAX(int,a,b));
    
    return 0;
}

程序输出

5

--------------------------------
Process exited after 0.02871 seconds with return value 0
请按任意键继续. . .

这种做法很明显,就是指定需要比较的类型,可以通过这个方法规避因为类型的问题引入错误。

— — C++ 的模板函数可以解决这个问题

模板函数出现后,这类问题就显得太简单了

#include 
#include 
 
using namespace std;
 
template 
inline T const& Max (T const& a, T const& b) 

    return a < b ? b:a; 
}

template 
inline T const& Min (T const& a, T const& b) 

    return b < a ? b:a; 
}
 
int main ()
{
 
    int i = 2;
    int j = -1;
    cout << "Max(i, j): " << Max(i, j) << endl; 
    cout << "Min(i, j): " << Min(i, j) << endl; 
 
    double f1 = 13.5; 
    double f2 = 20.7; 
    cout << "Max(f1, f2): " << Max(f1, f2) << endl;
 cout << "Min(f1, f2): " << Min(f1, f2) << endl; 
 
    string s1 = "Hello"
    string s2 = "World"
    cout << "Max(s1, s2): " << Max(s1, s2) << endl; 
    cout << "Min(s1, s2): " << Min(s1, s2) << endl;
 
   return 0;
}

程序输出

Max(i, j): 2
Min(i, j): -1
Max(f1, f2): 20.7
Min(f1, f2): 13.5
Max(s1, s2): World
Min(s1, s2): Hello

--------------------------------
Process exited after 0.1118 seconds with return value 0
请按任意键继续. . .

最后说一句,相同的东西比较才有意义,不同的东西,比较没有多大意思。

函数模板,如果理解的不深刻,可以看看书籍


推荐阅读:
专辑|Linux文章汇总
专辑|程序人生
专辑|C语言
我的知识小密圈

浏览 4
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报