一元减去0x80000000(有符号和无符号)

n3337.pdf草案5.3.1.8规定:

一元运算符的操作数应具有算术或无范围的枚举类型,结果是其操作数的否定。 对整数或枚举操作数执行整体提升。 无符号数量的负数是通过从2ⁿ减去其值来计算的,其中n是提升操作数中的位数。 结果的类型是提升的操作数的类型。

在某些情况下,这就足够了。 假设unsigned int是32位宽,那么(-(0x80000000u)) == 0x80000000u ,不是吗?

尽管如此,我找不到关于无符号0x80000000上的一元减号的任何内容。 此外,C99标准草案n1336.pdf,6.5.3.3似乎没有提及:

一元运算符的结果是其(提升的)操作数的否定。 整数提升在操作数上执行,结果具有提升类型。

UPDATE2:我们假设unsigned int是32位宽。 所以,问题是:C中的一元减号(有符号和无符号),以及C ++中的一元减号(仅限签名)?

UPDATE1:运行时行为和编译时行为(即常量折叠)都很有趣。

(相关: 为什么是abs(0x80000000)== 0x80000000? )

对于您的问题,您所包含的报价的重要部分是:

无符号数量的负数是通过从2ⁿ减去其值来计算的,其中n是提升操作数中的位数。

那么,要知道-0x80000000u的值是什么,我们需要知道n0x80000000u类型的位数。 这至少是32,但这是我们所知道的(没有关于实现中类型大小的进一步信息)。 给定一些n值,我们可以计算结果:

 n | -0x80000000u ----+-------------- 32 | 0x80000000 33 | 0x180000000 34 | 0x380000000 48 | 0xFFFF80000000 64 | 0xFFFFFFFF80000000 

(例如, unsigned int为16位且unsigned long为64位的实现将具有64的n )。


C99在§6.2.5类型p9中隐藏了相同的措辞:

涉及无符号操作数的计算永远不会溢出,因为无法通过生成的无符号整数类型表示的结果将以比结果类型可以表示的最大值大1的数量为模。

除了零之外的无符号操作数上的一元运算符的结果将始终被此规则捕获。

对于32位int0x80000000的类型将是unsigned int ,无论是否缺少u后缀,因此结果仍然是值为0x80000000且类型为unsigned int

如果您使用十进制常量2147483648 ,它将具有long类型并且计算将被签名。 结果将是值为-2147483648 ,类型为long

在n1336,6.3.1.3有符号和无符号整数中,第2段定义了转换为无符号整数:

否则,如果新类型是无符号的,则通过重复地添加或减去一个可以在新类型中表示的最大值来转换该值,直到该值在新类型的范围内。

因此,对于32位无符号整数, -0x80000000u==-0x80000000 + 0x100000000==0x80000000u