12

我正在处理一些工作中的代码,其中包括表单的表达式

-(sizeof(struct foo))

即 a 的否定,size_t当他们看到这一点时,我不清楚 C 和 C++ 标准对编译器的要求。具体来说,从这里和其他地方环顾四周,sizeof返回类型为 的无符号整数值size_t。在否定无符号整数时,我找不到指定行为的任何明确参考。有没有,如果有,是什么?

编辑:好的,所以关于无符号类型的算术有一些很好的答案,但不清楚这实际上是否如此。当这否定时,它是对无符号整数进行操作,还是转换为有符号类型并对其进行处理?从标准中期望的行为是否“想象它是相似幅度的负数,然后对无符号值应用'溢出'规则”?

4

6 回答 6

21

ISO C 和 ISO C++ 标准都保证无符号算术是模 2 n - 即,对于任何溢出或下溢,它都会“回绕”。对于 ISO C++,这是 3.9.1[basic.fundamental]/4:

声明的无符号整数unsigned应遵守算术模 2 n的定律,其中n是该特定整数大小的值表示中的位数。41

...

41) 这意味着无符号算术不会溢出,因为不能由得到的无符号整数类型表示的结果以比得到的无符号整数类型可以表示的最大值大一的数字为模减少。

对于 ISO C(99),它是 6.2.5/9:

涉及无符号操作数的计算永远不会溢出,因为无法由结果无符号整数类型表示的结果会以比结果类型可以表示的最大值大一的数字为模减少。

这意味着结果保证与SIZE_MAX - (sizeof(struct foo)) + 1.


在 ISO 14882:2003 5.3.1.7 中:

[...] 无符号量的负数是通过从 2 n中减去其值来计算的,其中 n是提升操作数中的位数。结果的类型是提升的操作数的类型。

于 2009-08-12T22:19:19.240 回答
2

http://msdn.microsoft.com/en-us/library/wxxx8d2t%28VS.80%29.aspx

无符号量的一元求反是通过从 2 n中减去操作数的值来执行的,其中 n 是给定无符号类型的对象中的位数。(Microsoft C++ 在使用补码算法的处理器上运行。在其他处理器上,求反算法可能不同。)

换句话说,确切的行为将是特定于架构的。如果我是你,我会避免使用这种奇怪的结构。

于 2009-08-12T22:14:46.567 回答
2

对无符号数求反对于在整个字中传播 lsb 以形成后续按位运算的掩码很有用。

于 2009-12-04T16:23:12.203 回答
1

我唯一能想到的就是错得让人头疼……

size_t size_of_stuff = sizeof(stuff);

if(I want to subtract the size)
    size_of_stuff = -sizeof(stuff);

size_t total_size = size_of_stuff + other_sizes;

溢出是一个功能!

于 2009-08-12T22:30:04.100 回答
1

当前的 C++ 草案标准,第 5.3.1 节第 8 句:

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

因此,生成的表达式仍然是无符号的,并且按照描述进行计算。

用户@outis 在评论中提到了这一点,但我将把它放在答案中,因为 outis 没有。如果 outis 回来回答,我会接受。

于 2009-08-12T22:47:27.980 回答
1

size_t是实现定义的无符号整数类型。

取反一个size_t可能会给你一个size_t具有通常无符号模数行为的类型结果。例如,假设size_t是 32 位sizeof(struct foo) == 4,则-sizeof(struct foo) == 4294967292,或 2 32 -4。

除了一件事:一元运算-符将整数提升(C) 或整数提升(C++)(它们本质上是同一件事)应用于其操作数。如果size_t至少和 一样宽int,那么这个提升什么也不做,结果是 类型size_t。但如果int比 宽size_t,那么INT_MAX >= SIZE_MAX,则 的操作数从to-被“提升” 。在这种不太可能的情况下,.size_tint-sizeof(struct foo) == -4

如果您将该值分配回一个size_t对象,那么它将被转换回size_t,从而产生SIZE_MAX-4您期望的值。但如果没有这样的转换,你会得到一些令人惊讶的结果。

现在我从未听说size_t过窄于 的实现int,因此您不太可能遇到这种情况。但这里有一个测试用例,unsigned short用作假设的窄size_t类型的替代,它说明了潜在的问题:

#include <iostream>
int main() {
    typedef unsigned short tiny_size_t;
    struct foo { char data[4]; };
    tiny_size_t sizeof_foo = sizeof (foo);
    std::cout << "sizeof (foo) = " << sizeof (foo) << "\n";
    std::cout << "-sizeof (foo) = " << -sizeof (foo) << "\n";
    std::cout << "sizeof_foo = " << sizeof_foo << "\n";
    std::cout << "-sizeof_foo = " << -sizeof_foo << "\n";
}

我的系统(具有 16-bit short、 32-bitint和 64-bit size_t)上的输出是:

sizeof (foo) = 4
-sizeof (foo) = 18446744073709551612
sizeof_foo = 4
-sizeof_foo = -4
于 2015-11-10T01:12:40.517 回答