问题提出:
在研究 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: “=”: 左操做数必须为左值.
可能的错误也就能够避免了. 也许也就拯救了地球说不定...