dub*_*jim 9 c c99 bitwise-operators undefined-behavior language-lawyer
这个问题是关于C标准需要什么,而不是主流编译器可以可靠地期望做什么.让我们关注C99,尽管有关C89/ANSI或C11如何分歧的输入也会受到欢迎.
第6.2.6.2节介绍了整数类型的"负0"的概念,它将0x8000...
在使用整数的符号和幅度表示的0xFFFF...
系统中,或者在使用一个补码表示的系统中.(该标准还允许此类系统将这些位模式用于陷阱值而不是负0;并且使用显性/更常见的二进制补码表示的系统没有负0.)
在该部分的进一步说,该标准说:
如果实现支持负零,则只能通过以下方式生成它们:
- 的
&
,|
,^
,~
,<<
,和>>
带参数的运算符产生这样的值- ...
未指定这些情况是否实际产生负零或正常零,以及当存储在对象中时负零是否变为正常零.
如果实现不支持负零的行为
&
,|
,^
,~
,<<
,和>>
运营商的参数会产生这样的价值是不确定的.
据我所知,标准没有进一步说明"产生这种价值的论据"的含义.但是,阅读本文的自然方式表明,任何类似于x | y
您期望结果的操作0x8000...
或0xFFFF...
实际上符合标准的操作都具有未定义的行为.
咦?真?那么根据标准,即使在二进制补码机上,~0
真的会导致不确定的行为吗?那疯狂.这不是预期的解释.
它们是否意味着:在使用符号和幅度或一个补码表示的系统上,并且没有负零,那么按位运算的行为将产生如果这些系统将为负零的位模式有一个未定义.在具有两个补码表示的系统上,此处的标准保持沉默?
如果你在标准中进一步展望(第6.5.7节),它会告诉我们:
uint32_t x = 1;
int32_t y = 1;
x << -1; /* is undefined, so is y << -1 */
x << 32; /* is undefined, so is y << 32 */
y << 31; /* shifting 1 into sign bit is also undefined */
((int32_t) -1) << 1; /* is undefined */
((int32_t) -1) >> 1; /* is implementation-defined */
Run Code Online (Sandbox Code Playgroud)
(这里我假设输入为int
s 的小整数文字的排名不高于int32_t
和uint32_t
.)
在这些后面的章节中,该标准没有说什么具体的操作使用时约&
,|
,^
,或~
将是不确定的.(它只是说在操作数上执行"通常的算术转换".如果转换提升为有符号类型某些超出范围的无符号值,并且如果我正确理解转换规则,则只会导致未定义的行为,这永远不会发生.)