简体   繁体   English

什么应该发生在size_t的否定(即`-sizeof(struct foo)`))?

[英]What should happen to the negation of a size_t (i.e. `-sizeof(struct foo)`))?

I'm dealing with some code at work that includes an expression of the form 我正在处理一些包含表单表达式的代码

-(sizeof(struct foo))

ie the negation of a size_t , and I'm unclear on what the C and C++ standards require of compilers when they see this. size_t的否定,我不清楚编译器在看到这个时C和C ++标准需要什么。 Specifically, from looking around here and elsewhere, sizeof returns an unsigned integral value of type size_t . 具体来说,从查看此处和其他地方, sizeof返回size_t类型的无符号整数值。 I can't find any clear reference for specified behavior when negating an unsigned integer. 当否定无符号整数时,我找不到任何指定行为的明确引用。 Is there any, and if so, what is it? 有没有,如果有的话,它是什么?

Edit: Ok, so there are some good answers regarding arithmetic on unsigned types, but it's not clear that this is in fact such. 编辑:好的,所以关于无符号类型的算术有一些很好的答案,但事实并非如此。 When this negates, is it operating on an unsigned integer, or converting to a signed type and doing something with that? 如果否定,它是在无符号整数上运行,还是转换为有符号类型并对其执行某些操作? Is the behavior to expect from the standards "imagine it's the negative number of similar magnitude and then apply the 'overflow' rules for unsigned values"? 从标准中预期的行为是“想象它是相似幅度的负数,然后对无符号值应用'溢出'规则”?

Both ISO C and ISO C++ standards guarantee that unsigned arithmetic is modulo 2 n - ie, for any overflow or underflow, it "wraps around". ISO C和ISO C ++标准都保证无符号算术模2 n - 即,对于任何上溢或下溢,它都“包裹”。 For ISO C++, this is 3.9.1[basic.fundamental]/4: 对于ISO C ++,这是3.9.1 [basic.fundamental] / 4:

Unsigned integers, declared unsigned , shall obey the laws of arithmetic modulo 2 n where n is the number of bits in the value representation of that particular size of integer. 声明unsigned符号的无符号整数应遵守算术模2 n的定律,其中n是该特定整数大小的值表示中的位数。 41 41

... ...

41) This implies that unsigned arithmetic does not overflow because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting unsigned integer type. 41)这意味着无符号算术不会溢出,因为无法由结果无符号整数类型表示的结果以比模式生成的无符号整数类型所表示的最大值大1的数量减少。

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

A computation involving unsigned operands can never overflow, because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting type. 涉及无符号操作数的计算永远不会溢出,因为无法通过生成的无符号整数类型表示的结果将以比结果类型可以表示的最大值大1的数量为模。

Which means the result is guaranteed to be the same as SIZE_MAX - (sizeof(struct foo)) + 1 . 这意味着结果保证与SIZE_MAX - (sizeof(struct foo)) + 1相同SIZE_MAX - (sizeof(struct foo)) + 1


In ISO 14882:2003 5.3.1.7: 在ISO 14882:2003中5.3.1.7:

[...] The negative of an unsigned quantity is computed by subtracting its value from 2 n , where n is the number of bits in the pro- moted operand. [...]无符号数量的负数是通过从2 n减去其值来计算的,其中n是所提出的操作数中的位数。 The type of the result is the type of the promoted operand. 结果的类型是提升的操作数的类型。

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

Unary negation of unsigned quantities is performed by subtracting the value of the operand from 2 n , where n is the number of bits in an object of the given unsigned type. 通过从2 n中减去操作数的值来执行无符号量的一元否定,其中n是给定无符号类型的对象中的位数。 (Microsoft C++ runs on processors that utilize two's-complement arithmetic. On other processors, the algorithm for negation can differ.) (Microsoft C ++在使用二进制补码算法的处理器上运行。在其他处理器上,否定算法可能不同。)

In other words, the exact behavior will be architecture-specific. 换句话说,确切的行为将是特定于体系结构的。 If I were you, I would avoid using such a weird construct. 如果我是你,我会避免使用这种奇怪的结构。

否定无符号数对于在单词上传播lsb以形成后续按位运算的掩码非常有用。

The only thing I can think of is so wrong it makes my head hurt... 我能想到的唯一一件事就是如此错误让我头疼...

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;

Overflow is a feature! 溢出是一个功能!

From the current C++ draft standard , section 5.3.1 sentence 8: 当前的C ++草案标准 ,第5.3.1节第8句:

The operand of the unary - operator shall have arithmetic or enumeration type and the result is the negation of its operand. 一元的操作-操作员应算术或枚举类型,其结果是其操作数的否定。 Integral promotion is performed on integral or enumeration operands. 对整数或枚举操作数执行整体提升。 The negative of an unsigned quantity is computed by subtracting its value from 2 n , where n is the number of bits in the promoted operand. 通过从2 n中减去其值来计算无符号数量的负数,其中n是提升的操作数中的位数。 The type of the result is the type of the promoted operand. 结果的类型是提升的操作数的类型。

So the resulting expression is still unsigned and calculated as described. 因此,得到的表达式仍然是无符号的,并按所述计算。

User @outis mentioned this in a comment, but I'm going to put it in an answer since outis didn't. 用户@outis在评论中提到了这一点,但我会把它放在答案中,因为outis没有。 If outis comes back and answers, I'll accept that instead. 如果outis回来并回答,我会接受。

size_t is an implementation-defined unsigned integer type. size_t是实现定义的无符号整数类型。

Negating a size_t value probably gives you a result of type size_t with the usual unsigned modulo behavior. 否定size_t可能会为您提供类型size_t的结果,其中包含通常的无符号模数行为。 For example, assuming that size_t is 32 bits and sizeof(struct foo) == 4 , then -sizeof(struct foo) == 4294967292 , or 2 32 -4. 例如,假设size_t为32位且sizeof(struct foo) == 4 ,则-sizeof(struct foo) == 4294967292或2 32 -4。

Except for one thing: The unary - operator applies the integer promotions (C) or integral promotions (C++) (they're essentially the same thing) to its operand. 除了一两件事:一元-操作员施加整数促销 (C)或整体促销 (C ++)(它们基本上是相同的东西),以它的操作数。 If size_t is at least as wide as int , then this promotion does nothing, and the result is of type size_t . 如果size_t至少与int一样宽,则此促销不执行任何操作,结果的类型为size_t But if int is wider than size_t , so that INT_MAX >= SIZE_MAX , then the operand of - is "promoted" from size_t to int . 但是,如果int比更宽size_t ,使得INT_MAX >= SIZE_MAX ,然后的操作数-是由“提升” size_tint In that unlikely case, -sizeof(struct foo) == -4 . 在那种不太可能的情况下, -sizeof(struct foo) == -4

If you assign that value back to a size_t object, then it will be converted back to size_t , yielding the SIZE_MAX-4 value that you'd expect. 如果将该值分配回size_t对象,则它将转换回size_t ,从而产生您期望的SIZE_MAX-4值。 But without such a conversion, you can get some surprising results. 但如果没有这样的转换,您可以获得一些令人惊讶的结果。

Now I've never heard of an implementation where size_t is narrower than int , so you're not likely to run into this. 现在我从来没有听说过size_tint更窄的实现,所以你不太可能碰到这个。 But here's a test case, using unsigned short as a stand-in for the hypothetical narrow size_t type, that illustrates the potential problem: 但这是一个测试用例,使用unsigned short作为假设的narrow 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";
}

The output on my system (which has 16-bit short , 32-bit int , and 64-bit size_t ) is: 我的系统上的输出(具有16位short ,32位int和64位size_t )是:

sizeof (foo) = 4
-sizeof (foo) = 18446744073709551612
sizeof_foo = 4
-sizeof_foo = -4

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM