宏比较值,坑的一B

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

#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 <stdio.h>#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 <iostream>
#include <string>using namespace std;template <typename T>
inline T const& Max (T const& a, T const& b) 
{ return a < b ? b:a; 
}template <typename T>
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语言

我的知识小密圈

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/466867.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux下Samba服务器搭建

linux文件共享之samba服务器 ——ubuntu 宗旨&#xff1a;技术的学习是有限的&#xff0c;分享的精神是无限的。 关闭LINUX防火墙命令&#xff1a; #ufwdisable 然后就在windows下ping一下linux的IP&#xff0c;如果能ping通&#xff0c;就可以继续下面的内容&#xff0c;如果p…

搞懂C++为什么难学,看这篇就够了!

学C能干什么&#xff1f; 往细了说&#xff0c;后端、客户端、游戏引擎开发以及人工智能领域都需要它。往大了说&#xff0c;构成一个工程师核心能力的东西&#xff0c;都在C里。跟面向对象型的语言相比&#xff0c;C是一门非常考验技术想象力的编程语言&#xff0c;因此学习起…

看图学源码之FutureTask

RunnableFuture 源码学习&#xff1a; 成员变量 任务的运行状态的转化 package java.util.concurrent; import java.util.concurrent.locks.LockSupport;/**可取消的异步计算。该类提供了Future的基本实现&#xff0c;包括启动和取消计算的方法&#xff0c;查询计算是否完成以…

单片机的引脚,你都清楚吗?

第1课&#xff1a;单片机简叙1.单片机可以做什么&#xff1f;目前单片机渗透到我们生活的各个领域&#xff0c;几乎很难找到哪个领域没有单片机的踪迹。小到电话&#xff0c;玩具&#xff0c;手机&#xff0c;各类刷卡机&#xff0c;电脑键盘&#xff0c;彩电&#xff0c;冰箱&…

Graphviz的安装及纠错

在Anaconda Prompt里边输入conda install graphviz 安装成功之后输入pip install graphviz 它会提示成功安装。 启动 Jupyter Notebook &#xff0c;在文件里边输入 import graphviz 测试&#xff0c;如果没有报错证明&#xff0c;模块安装成功&#xff0c;但是在运行程序…

sklearn——决策树

总结sklearn决策树的使用&#xff0c;方便以后查阅。1.分类决策树 &#xff08;基于CART树&#xff09; 原型&#xff1a;参数&#xff1a;2、回归分类树 原型&#xff1a;参数&#xff1a;3、export_graphviz 当训练完毕一颗决策树时&#xff0c;可以通过sklearn.tree.expor…

Linux下SVN服务器的搭建

Linux下SVN服务器的搭建 宗旨&#xff1a;技术的学习是有限的&#xff0c;分享的精神是无限的。 1、下载工具&#xff08;下载地址&#xff1a;&#xff09; subversion-1.6.1.tar.gz subversion-deps-1.6.1.tar.gz 2、解压两个包&#xff1a; a) tar -xzvf subvers…

记一次解决问题的掉坑过程

这两天在调试一个音频ADC 芯片&#xff0c;也是之前的项目&#xff0c;但是一直调不出来&#xff0c;我发现我总是在这样的问题上纠结很久&#xff0c;以前踩过的坑后面照样会踩&#xff0c;只不过踩完会迅速把脚拉出来继续前进&#xff0c;我经常听到有人说「做嵌入式真的太容…

sklearn——AdaBoost应用

选自《python大战机器学习》

面试常见的C语言字符串操作

#字符串倒序输出实现逻辑&#xff0c;通过strlen获取字符串长度&#xff0c;然后通过 len/2 进行交叉赋值&#xff0c;这里需要注意&#xff0c;不需要考虑len是奇数还是偶数的问题。如果len是奇数&#xff0c;最后一个字符就不需要倒序&#xff0c;如果是偶数&#xff0c;最后…

HttpHandler:给指定路径下的图片添加水印显示

圣诞节&#xff0c;25日&#xff0c;要交ACCP5.0认证的项目&#xff0c;其中有这样一个要求&#xff1a;书店的所有图书的封面放在了\images\convers\下面&#xff0c;要求所有引用这一路径下的图片都添加书店的店名水印图片。就是说拦截Http请求了&#xff0c;自然想到HttpHan…

Linux 下的复制命令,这几个比较靠谱

平时我们使用Linux复制命令的时候&#xff0c;一般使用 cp命令&#xff0c;但是cp 命令性能比较令人担忧使用tar 命令来拷贝大量文件通过对比下面的几个命令&#xff0c;在拷贝比较多而且比较大的文件的话&#xff0c;用git clone 比较靠谱&#xff0c;特别是复制代码库&#x…

Mendeley文献管理软件使用介绍

<!DOCTYPE html>New DocumentMendeley 是一款免费的跨平台文献管理软件&#xff0c;同时也是一个在线的学术社交网络平台。Mendeley 对 PDF、Bibtex 的支持非常好&#xff0c;可以直接导出 Bibtex 格式&#xff0c;还可以直接导入zotero数据库&#xff0c;决定了其兼容性…

过拟合问题——正则化方法

看了很多资料&#xff0c;本身想放一个正则化的概念的&#xff0c;实在不敢放&#xff0c;怕吓跑一堆人&#xff0c;所以&#xff0c;将就吧。首先&#xff0c;我们知道正则化&#xff08;Regularization&#xff09;是解决过拟合问题的&#xff0c;简单来说&#xff0c;过拟合…

CentOS 8明年正式停止维护,以后再也不会有免费的RHEL了!

CentOS 8 明年正式停止维护&#xff0c;以后再也不会有免费的 RHEL 了!CentOS 是 Community Enterprise Operating System&#xff08;社区企业操作系统&#xff09;的首字母缩写&#xff0c;是 100&#xff05; 重建的 RHEL&#xff08;红帽企业 Linux&#xff09;。尽管 RHEL…

Python sqlalchemy orm 多外键关联

多外键关联 注&#xff1a;在两个表之间进行多外键链接 如图&#xff1a; 案例&#xff1a; # 创建两张表并添加外键主键 # 调用Column创建字段 加类型 from sqlalchemy import Integer, ForeignKey, String, Column# 调用基类Base from sqlalchemy.ext.declarative import dec…

将DataFrame格式的数据存入到mysql数据库中

因为最近频繁操作数据库&#xff0c;特别是写入数据比较麻烦。在DataFrame格式或者是Series格式的数据处理之后&#xff0c;总是会面临写入数据&#xff0c;迫不得已只能进行格式转换&#xff0c;搜索过程中发现了to_sql&#xff08;&#xff09;函数&#xff0c;就百度了用法&…

从小米智能家居入手,揭秘物联网关键技术

物联网已不知不觉融入我们的生活中给我们带来便捷&#xff0c;比如&#xff0c;智能门锁、ETC 电子自动收费系统等&#xff0c;一开始感觉还挺很新奇的&#xff0c;现在也习以为常了。那到底什么是物联网&#xff1f;可能很多人还挺蒙圈的。所谓物联网&#xff0c;最终目的就是…

Orange——The Data

The Data 这个部分描述的是怎样在Orange上加载数据。我们也将展示如何探索数据&#xff0c;发现一些基本统计特性&#xff0c;怎么数据取样。 Data Input Orange可以读取本机以制表符分隔的格式的文件&#xff0c;也可以从任何主要的标准电子表格文件类型加载数据&#xff0…

[转载]VirtualBox网络配置详解

标题: [原创]VirtualBox网络配置详解来自 http://www.linuxsir.org/bbs/showthread.php?p1800679#post1800679 欢迎转载, 转载请注明作者, 谢谢下面简单介绍一下Test-bed Environment:Host : 偶的本本OS : Arch Linux (Kernel Version 2.6.20) 已安装uml_utilities(包含tunctl…