問題提出:
在研究 emacs lisp 的時候, 文件 lisp.h 裏面有一個宏的寫法有點特殊, 引發了個人奇怪. 性能
#define XFLOAT_DATA(f) (0 ? XFLOAT (f)->u.data : XFLOAT (f)->u.data) 優化
其中爲了簡化問題, 咱們將這個宏簡寫一下: 編譯器
#define XFLOAT_DATA(f) (0 ? f->data : f->data) emacs
在宏中使用了三元操做符 "? :", 但先後的值同樣, 而且判斷條件是常量 0, 這是怎麼回事?
難道做者閒得無聊才這麼作麼? 問題可不這麼簡單吧... 編譯
問題思考和解決: 程序
再參考一下另外一個宏, 也許對判斷做者的意圖有所啓發: error
#define SSIZE(str) (str->size + 0) lisp
在這個宏中, size 是一個整形字段, 因此加上 0 並不會改變最後的 size 值, 不過這個表達式
使得 SSIZE(str) 成爲右值 (rvalue) 表達式了, 因此其能夠讀取, 但不能寫入了: 文件
int len = SSIZE(str); // 合法, SSIZE(str) 是做爲右值
SSIZE(str) = len; // 編譯錯誤, SSIZE(str) 即 (str->size + 0) 不能做爲左值. 思考
這樣, 咱們猜想將 XFLOAT_DATA() 宏寫成那個樣子, 也是有着一樣的意圖的.
但沒有使用 (f->data + 0) 的卻另有緣由了. 由於編譯器對於整形運算能夠施加各類優化,
如 size+0 可直接優化爲 size, 可是對於浮點數 f+0 卻不必定作這樣的優化.
若是宏寫爲 (f->data + 0) 會帶來額外的運行時開銷, 這種開銷是難以接受的, 對於到處
考慮性能的 C/C++ 程序猿而言.
因此, 寫成了 (0 ? f->data : f->data) 這種奇怪的表達式方式了.
但是事情還沒完, 老編譯器不能將 "?:" 表達式做爲左值使用, 可是新編譯器卻能夠:
int x = 1, y, z;
(x ? y : z) = 3; // 居然合法, 結果是 y = 3.
XFLOAT_DATA(f) = 3.14; // 居然合法! 做者意圖未達到...
因此, 辛苦寫爲 (0 ? f->data : f->data) 沒法達到阻止左值的意圖, 若是做者真的惟一意圖如此的話.
最後, 考慮的一種解決方式仍是用咱們的老朋友 const 關鍵字:
#define XFLOAT_DATA(f) ((const double) (f->data))
這下表達式 "XFLOAT_DATA(f) = 3.14" 編譯器終於能夠阻止其編譯了.
error C2106: 「=」: 左操做數必須爲左值.
可能的錯誤也就能夠避免了. 也許也就拯救了地球說不定...