在宏中使得字段只能讀取 (幾何畫板開發筆記 三)

問題提出:
   在研究 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: 「=」: 左操做數必須爲左值.

可能的錯誤也就能夠避免了. 也許也就拯救了地球說不定...

相關文章
相關標籤/搜索