【发布时间】:2016-04-20 17:53:22
【问题描述】:
简单代码sn-p:
#define FOO 7
int bar = -875;
bar <<= FOO;
UBSAN 将其报告为 UB。
我的理解是-875 << 7 就是-(875<<7)
并且没有溢出。
那么,这里真的有问题吗?
【问题讨论】:
标签: c bit-manipulation undefined-behavior ubsan
简单代码sn-p:
#define FOO 7
int bar = -875;
bar <<= FOO;
UBSAN 将其报告为 UB。
我的理解是-875 << 7 就是-(875<<7)
并且没有溢出。
那么,这里真的有问题吗?
【问题讨论】:
标签: c bit-manipulation undefined-behavior ubsan
你的理解不正确。
首先你使用bar <<= FOO 语法。这显式地将bar 和bar 转换为负数。负值的左移会在 C 中产生未定义的行为。bar <<= FOO 无法解释为-(875<<7)。
其次,关于-875 << 7 在运算符优先级方面:一元运算符的优先级总是高于二元运算符,这意味着-875 << 7 是(-875) << 7 而不是-(875 << 7)。同样,负值的左移会在 C 中产生未定义的行为。
【讨论】:
在符号幅度机器上,尚不清楚左移负数的效果应该是什么,如果尝试这样的操作,这样的机器陷入陷阱也不是不合理的。在这样的机器上,对负整数左移的行为施加任何要求可能会要求此类机器的编译器生成额外的代码,即使在要移位的值始终为正的情况下也是如此。为避免征收此类费用,该标准的作者拒绝强制执行任何特定行为。
1 的补码和双的补码平台在移动负值时没有逻辑上的陷阱(尽管 -1除非作者故意迟钝。
大概在 2005 年左右之前,对于仅在普通的二进制补码机器上使用负值左移运算符调用的代码,甚至没有任何可以想象的不安全性。不幸的是,大约在那个时候,一个想法开始占据主导地位,该想法表明,避免执行标准未强制执行的任何操作的编译器可能比在标准未强制执行的情况下表现有用的编译器更“有效”,并且这种“效率” "是可取的。我还不知道编译器关于声明 y=x<<1; 追溯性地使 x 的值非负,但我不相信有任何理由相信他们不会这样做因此,除非或直到某个机构正式编纂了主流微型计算机 C 编译器一致支持 25 年以上的行为保证,否则此类代码不能被视为“安全”。
【讨论】: