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