宏比较值,坑的一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,因為long在unsigned 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語言
我的知識小密圈
總結
- 上一篇: 搞懂进程组、会话、控制终端关系,才能明白
- 下一篇: IceGrid应用 配置手册