为什么-1大于sizeof(int)?

20

考虑以下代码:

template<bool> class StaticAssert;
template<> class StaticAssert<true> {};
StaticAssert< (-1 < sizeof(int)) > xyz1; // Compile error
StaticAssert< (-1 > sizeof(int)) > xyz2; // OK
为什么 -1 > sizeof(int) 会是 true?
  1. 是否正确的理解为 -1 被提升为 unsigned(-1),再比较 unsigned(-1) > sizeof(int)
  2. 是否正确的理解为当 sizeof(int) 等于 4 时,-1 > sizeof(int) 等价于 -1 > size_t(4)。如果这样的话,为什么 -1 > size_t(4) 是 false?
这符合 C++ 标准吗?
4个回答

15
以下是标准(ISO 14882)如何解释abort -1 > sizeof(int):
关系运算符“>”在5.9(表达式.rel/2)中定义。
进行算术或枚举类型的操作数上执行通常算术转换...这称为通常的算术转换,其定义如下:
如果任一操作数为long double类型,则进行该类型的计算。 否则,如果任一操作数为double类型,则进行double类型的转换。 否则,如果任一操作数为float类型,则进行float类型的转换。 否则,在两个操作数上执行整数提升。 ... 整数提升在4.5(conv.prom/1)中定义。
可以将char、signed char、unsigned char、short int或unsigned short int类型的rvalue转换为int类型的rvalue,如果int可以表示源类型的所有值,则源rvalue可以转换为unsigned int类型的rvalue。
sizeof的结果在5.3.3(expr.sizeof/6)中定义。结果是size_t类型的常量。
size_t在C标准(ISO 9899)中定义,它是无符号整数类型。
因此,对于-1 > sizeof(int),> 触发了通常的算术转换。通常的算术转换将-1转换为unsigned int,因为int不能表示size_t的所有值。根据平台的不同,-1变成一个非常大的数字。所以 -1 > sizeof(int) 是真的。

2
这可能只是一个笔误,但 size_t 是一种无符号整数类型,并不一定意味着 int 不能表示 size_t 的所有值(size_t 可能是 unsigned short),尽管在提问者的平台上显然不能这样做。 - CB Bailey
2
(unsigned T)-1 不仅仅是一个很大的值,它是 unsigned T 可以保存的最大值 - GManNickG
1
我非常清楚标准允许的内容。:) -1始终是最大的,请阅读转换规则。或者参考这个链接:https://dev59.com/bHRA5IYBdhLWcg3w1BmW#809341 - GManNickG
1
@GMan 感谢您的帮助。我误解了标准中的描述。(删除错误评论) - czchen

14
因为无符号整型比有符号整型更大,而当-1转换成无符号整型size_t时,实际上-1 == 0xFFFFFFFF > 4
这就是C++标准规定的工作方式。

编译器不会对这类情况发出警告吗? - kriss
@kriss - 不同的编译器会发出不同的警告。同时,警告可以通过编译器命令行选项和/或源代码中的编译指示来抑制;然后程序员也可以选择忽略这些警告。 - ChrisW
只有在使用补码(不确定是哪种)时才能这样做。 - rubenvb
1
不管怎样:根据标准,在任何地方,unsigned(-1) == UINT_MAX - MSalters
@Artyom:unsignedsigned更强。什么是“更强”?标准没有定义这个术语。 - Alexey Malistov

4

由于-1被转换为size_t,这是一种无符号数据类型,因此(size_t)-1 == 4294967295(在32位系统上)肯定大于4

例如,如果您将-Wall添加到gcc设置中,则会收到警告,提示您正在比较有符号和无符号数据类型


假设 sizeof(size_t) >= sizeof(int) 真的安全吗?也就是说,这个标准化了吗? - Wolf

2
这很简单但也很遗憾。在C/C++中:
  1. 大部分时间,无符号整数类型具有模整数的语义(它们表示等价类)
  2. 无符号整数类型的比较具有通常的整数顺序语义,因此 1U < 2U(即 0U 是最小的 unsigned 值)
  3. sizeof 的类型是 size_t
  4. size_t 是一个无符号整数类型
  5. 点(1)意味着涉及有符号和无符号整数的混合算术计算以无符号模算术方式进行:这是唯一的不违反“无符号意味着模”规则的可能性。将整数转换为等效于它的整数等价类是微不足道的。(而反过来需要选择一个整数来表示等价类。)
  6. 点(5)意味着 -1 < 1U 被解释为 unsigned(-1) < 1Uunsigned(-1) = - 1U,显然 - 1U < 1U,所以 -1 < 1U 是真的。
  7. 点(1、3、4)意味着 sizeof something 的作用(大多数情况下)类似于等价类(!)。
  8. 所有这些都意味着 -1 < sizeof something

结论:这是从C继承而来的设计错误。

规则:

只有在模算术、位操作(&|^<<>>~ 运算符)、字节操作(unsigned char 在 C/C++ 中表示“字节”)和字符(unsigned char 在 C/C++ 中表示字符)时才使用无符号类型。

不要使用无符号类型进行算术运算。

如果一个函数期望一个永远不应该为负数的整数值,请取一个有符号整数,并在函数中可选地检查该值是否在范围内。


我觉得第六点有点困惑,或许在“unsigned(-1) = -1U”中包含 == 会更好。 - Wolf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接