C++通常的算术转换不转换

C++ usual arithmetic conversions not converting

本文关键字:转换 C++      更新时间:2023-10-16

首先,我想指出的是,我确实阅读了一些标题大致相似的其他答案。然而,这些要么是指我认为是旧版本的标准,要么是指促销,而不是转换。

我一直在";C++速成课程";,作者在探索内置运算符的一章中陈述了以下内容:

如果没有任何浮点提升规则适用,则检查是否有任何一个参数签名。如果是,则两个操作数都变为有符号。最后,与浮点类型的提升规则一样最大的操作数用于提升另一个操作数:。。。

如果我正确阅读了标准,这不是真的,因为根据cppreference.com,

如果两个操作数都有符号或都无符号,则转换秩较小的操作数将转换为整数转换秩较大的操作数

否则,如果无符号操作数的转换秩大于或等于有符号操作数转换秩,则有符号操作数会转换为无符号操作数数的类型。

否则,如果有符号操作数的类型可以表示无符号操作数中的所有值,则无符号操作数会转换为有符号操作数数的类型

否则,两个操作数都将转换为有符号操作数类型的无符号对应对象。

更让我困惑的是以下代码:

printf("Size of int is %d bytesn", sizeof(int));
printf("Size of short is %d bytesn", sizeof(short));
printf("Size of long is %d bytesn", sizeof(long));
printf("Size of long long is %d bytesn", sizeof(long long));
unsigned int x = 4000000000;
signed int y = -1;
signed long z = x + y;
printf("x + y = %ldn", z);

产生以下输出:

Size of int is 4 bytes
Size of short is 2 bytes
Size of long is 8 bytes
Size of long long is 8 bytes
x + y = 3999999999

根据我对标准的理解,y本应转换为unsigned int,从而导致错误的结果。结果正确的,这使我假设在这种情况下不会发生转换。为什么?如果能就此事提出申诉,我将不胜感激。非常感谢。

(我也很感激有人告诉我,我在现实生活中永远不需要这种神秘的知识,即使这不是真的——只是为了让我安心。)

将负有符号值转换为无符号值会产生与其对应的二进制补码值。添加此无符号值将导致溢出,然后产生与添加负值完全相同的值。

顺便说一句:这就是处理器做减法的诀窍(或者我在现代错了吗?)。

如果我正确阅读了标准,这不是真正的

你是对的。CCC是错误的。

根据我的理解,y应该转换为无符号int,

确实如此。

结果是正确的,这让我假设在这种情况下不会发生转换。

你认为错了。

由于-1不是可表示的无符号数,它只是简单地转换为可表示的数,可表示的,并且与-1一致,以可表示值的数量为模。当你把它加到x上时,结果比任何可表示的值都大,所以结果是与模一致的可表示值。由于模运算的神奇之处,你可以得到正确的结果。

您添加了一个无符号int和一个有符号int,因此两个操作数的转换秩相同。因此,第二条规则将适用(强调我的):

[否则,]如果无符号操作数的转换秩大于或等于有符号操作数转换秩,则有符号操作数会转换为无符号操作数类型。

  • -1被转换为无符号类型,方法是向其添加比最高无符号int大2的最小幂(事实上,它以2的补码表示)
  • 这个数字加上400000000,结果是总和和2的幂的模(实际上丢弃了它的高位)
  • 而你只是得到了预期的结果

标准强制转换为无符号类型,因为无符号溢出是由标准定义的,而无符号溢出不是。

我也很感激有人告诉我我永远都不需要这个现实生活中的神秘知识

上面没有提到一个隐含的推广,这是错误的常见原因:

unsigned short x = 0xffff;
unsigned short y = 0x0001;
if ((x + y) == 0)

这样做的结果是错误的,因为算术运算被隐式提升到至少int。

从代码片段的第一个print语句的输出中可以明显看出,由于int的大小是4个字节,因此无符号int值的范围可以从0到4294967295。(2^32-1)

因此,以下声明完全有效:

unsigned int x = 4000000000;

请记住,将x声明为无符号整数会使其自身的(+ve)范围是有符号对应项的两倍,但它位于完全相同的范围内,因此它们之间的转换是可能的。

signed int y = -1;
signed long z = x + y;
printf("x + y = %ldn", z);

因此,对于上面代码的其余部分,y和z的操作数是有符号(+ve/-ve)还是无符号(+ve)在这里无关紧要,因为您首先要处理的是一个无符号整数,通过在有符号版本中添加UINTMAX+1,将有符号整数转换为相应的无符号整数版本。这里UINTMAX代表最大的无符号值,它与最小的2次方相加(2^0=1)。最终结果显然会大于我们可以存储的值,因此它被视为模来收集正确的无符号整数。

但是,如果您要将x转换为已签名:

signed int x = 4000000000;
int y = -1;
long z = x + y;
printf("x + y = %ldn", z);

你会得到你可能期望的:

x + y = -294967297