【问题标题】:UBSAN reports: -875 << 7 as undefined behaviourUBSAN 报告:-875 << 7 作为未定义的行为
【发布时间】:2016-04-20 17:53:22
【问题描述】:

简单代码sn-p:

#define FOO 7
int bar = -875;
bar <<= FOO;

UBSAN 将其报告为 UB。

我的理解是-875 &lt;&lt; 7 就是-(875&lt;&lt;7) 并且没有溢出。

那么,这里真的有问题吗?

【问题讨论】:

    标签: c bit-manipulation undefined-behavior ubsan


    【解决方案1】:

    你的理解不正确。

    首先你使用bar &lt;&lt;= FOO 语法。这显式地将barbar 转换为负数。负值的左移会在 C 中产生未定义的行为。bar &lt;&lt;= FOO 无法解释为-(875&lt;&lt;7)

    其次,关于-875 &lt;&lt; 7 在运算符优先级方面:一元运算符的优先级总是高于二元运算符,这意味着-875 &lt;&lt; 7(-875) &lt;&lt; 7 而不是-(875 &lt;&lt; 7)。同样,负值的左移会在 C 中产生未定义的行为。

    【讨论】:

    • 谢谢,@AnT。就我而言,我在执行移位时转换为 7 位定点。那么,负数转定点的正确方法是什么?
    • @Jacko:乘以它而不是移位?将它乘以 128。编译器仍然可以为此生成一个转换(如果这确实是最有效的方法),但您不必通过 UB。
    • 来自 C11 标准:6.5.7 位移位运算符 [...] E1
    【解决方案2】:

    在符号幅度机器上,尚不清楚左移负数的效果应该是什么,如果尝试这样的操作,这样的机器陷入陷阱也不是不合理的。在这样的机器上,对负整数左移的行为施加任何要求可能会要求此类机器的编译器生成额外的代码,即使在要移位的值始终为正的情况下也是如此。为避免征收此类费用,该标准的作者拒绝强制执行任何特定行为。

    1 的补码和双的补码平台在移动负值时没有逻辑上的陷阱(尽管 -1除非作者故意迟钝。

    大概在 2005 年左右之前,对于仅在普通的二进制补码机器上使用负值左移运算符调用的代码,甚至没有任何可以想象的不安全性。不幸的是,大约在那个时候,一个想法开始占据主导地位,该想法表明,避免执行标准未强制执行的任何操作的编译器可能比在标准未强制执行的情况下表现有用的编译器更“有效”,并且这种“效率” "是可取的。我还不知道编译器关于声明 y=x&lt;&lt;1; 追溯性地使 x 的值非负,但我不相信有任何理由相信他们不会这样做因此,除非或直到某个机构正式编纂了主流微型计算机 C 编译器一致支持 25 年以上的行为保证,否则此类代码不能被视为“安全”。

    【讨论】:

    • 谢谢,@supercat。我正在学习更加尊重轮班操作员 :) 快速且可能致命。
    • @Jacko:希望有一天能在“sane C”和当前流行的“Obtuse C”之间出现一个公认的分歧,其中移位寄存器将没有副作用避免让程序员做超出标准要求的任何有用的事情的方法。
    • 关于最后一段,以下可能是有用的后续阅读:Wang, Xi, et al. “未定义行为检测的差异化方法。” ACM 59.3 (2016) 的通讯:99-106。 (online)
    • 2005 年发生了什么导致“未定义:=> 未执行”的趋势?我最近才意识到这一点,它伤害了我的大脑。我觉得程序员和编译器之间的不成文契约正在被打破。
    • @njuffa:让我恼火的是 gcc 维护者的态度,即依赖于标准未定义但每个现代编译器都 100% 一致对待的行为的代码应该被视为“损坏”,尤其是当修复代码会有效地阻止原本有用的优化时。如果 C 要作为一种有用的语言生存下去,它需要添加规范性规范,以便程序可以说明它们需要哪些极端情况行为,然后编译器可以接受程序(并遵守要求)或拒绝程序。跨度>
    猜你喜欢
    • 1970-01-01
    • 2021-09-14
    • 1970-01-01
    • 2013-09-29
    • 1970-01-01
    • 1970-01-01
    • 2021-08-02
    • 1970-01-01
    相关资源
    最近更新 更多