【发布时间】:2023-04-01 09:40:01
【问题描述】:
我阅读了this 很棒的太宽泛的问题,并且遇到了一些我以前不知道的 UB。
我不时看到 UB 的主要原因是在两个序列点之间两次更改变量。诸如:x = x++ 或 z = y++ + ++y;。阅读 在两个序列点之间两次更改变量是 UB 帮助我了解了这些情况下的根本原因。
但是像负片移位这样的事情呢? (int x = 8 << -1) 是否有规则可以解释这一点,或者我应该将其记住为独特的 UB 可能性?
我查看了here 并在 Integer Overflows 部分下发现了带有负数的位移,但我不明白它们为什么相关。当int 移位过多时,会导致溢出,但 IMO 移位负数只是 UB,问题不在于“超出边缘”的位...
也看过这里,但没有回答我的问题:
对每个操作数执行整数提升。结果的类型是提升的左操作数的类型。如果右操作数的值为负数或大于或等于提升的左操作数的宽度,则行为未定义。
所以我的问题是:
- 具体来说,负数移位是否被视为整数溢出?如果是,为什么?
- 如果不是,它是更大现象的一部分吗?
- 是否有(其他)独特案例不能归为一个根本原因?
【问题讨论】:
-
负移位未定义,超长移位也是如此(N 位整数类型的 N 位或更多位移位)。标准是这样说的。你必须知道它是这么说的。是的,有很多案例,将它们分组会很棘手。 C11 标准的附件 J.2 记录了第 557-571 页上的未定义行为(每页末尾只有几行,因此略多于 14 页)。定期阅读以了解未定义的内容。不;我也没记住。
-
@JonathanLeffler 这是一个令人印象深刻的列表,谢谢!虽然我希望有一些更容易记住的东西:)
-
回答你的问题 - 我希望一些更容易记住的东西 我的一般经验法则是 - 任何似乎会改变不同实现(目标、平台等)的行为) 是“发现” UB 的危险信号。然后我用清单确认。这是有道理的,因为标准是在抽象机器上定义的,而不是任何实现。因此,触摸实现的方面必须保持未定义。警惕实现定义的行为。
-
@Jean-BaptisteYunès 您非常有说服力的例子的一个更抽象的版本是“如果可能很难让硬件供应商同意这一点,或者很难明确定义并留出一些空间为了优化实现,那么它可能是UB”。这可能是 OP 正在寻找的“易于记忆的经验法则”。
-
UB 类型的优秀总结:blog.regehr.org/archives/1520
标签: c undefined-behavior