我有一个关于C中隐式类型转换的问题

wecizke3  于 2024-01-06  发布在  其他
关注(0)|答案(2)|浏览(127)
unsigned z  = 0x80000000;
long     z2 = -z;

printf("z  is %x, z  is %u\n", z, z);
printf("z2 is %lx, z2 is %ld\n", z2, z2);

字符串
打印结果如下:

z  is 80000000, z  is 2147483648 
z2 is 80000000, z2 is 2147483648


我想知道这份声明中发生了什么。

long z2 = -z;

332nm8kg

332nm8kg1#

我将把这个代码片段扩展到以下几个方面:

unsigned z  = 0x80000000;
unsigned z1 = -z;
long     z2 = -z;
int      z3 = -z;

printf( "z  is %x, z  is %u\n",   z,  z  );
printf( "z1 is %x, z1 is %u\n",   z1, z1 );
printf( "z2 is %lx, z2 is %ld\n", z2, z2 );
printf( "z3 is %x, z3 is %d\n",   z3, z3 );

个字符
z1有助于解释,并且有关于z3的问题。

-z有什么作用?

[C17一元-运算符的结果是它的(提升的)操作数的负数,整数提升是在操作数上执行的,并且结果具有提升的类型。
整数提升只对级别低于int的类型有效,因此在这里不适用。这意味着-z生成unsigned。因此-z生成-2,147,483,648(-231)作为unsigned

-231作为一个32位unsigned

[C17§6.3.1.3 ¶2]否则,如果新类型是无符号的,则通过在新类型中可以表示的最大值上重复加1或减1来转换值,直到该值在新类型的范围内。
在您的系统中,unsigned为4,294,967,295,因此-z的结果为-2,147,483,648+(4,294,967,295 + 1)* 1 = 2,147,483,648(231)。这就是我们在z1中看到的结果。

将231分配给64位long

[C17§6.3.1.3 ¶1]当一个整数类型的值被转换为除_Bool之外的另一个整数类型时,如果该值可以用新的类型表示,则该值不变。
接下来,我们将2,147,483,648赋值给z2。在您的系统中,long可以表示2,147,483,648,因此赋值给它的值保持不变。这就是我们看到的z2

将231分配给32位longint

[C17§6.3.1.3 ¶3]否则,新类型是带符号的,值不能在其中表示;结果要么是实现定义的,要么是产生了一个实现定义的信号。
接下来,我们将2,147,483,648赋值给z3。这是实现定义的行为。在您的系统上,它将与unsigned0x80000000)相同的字节模式赋值给int。这就是我们在z3上看到的。

dxxyhpgq

dxxyhpgq2#

一元-运算符的行为如下(C17 6.5.3.3):
一元运算符的结果是其(提升)操作数的负数。整数提升是在操作数上执行的,结果具有提升类型。
unsigned的情况下,它不是一个小整数类型(更多信息在这里:Implicit type promotion rules),所以它不受整数提升的影响。
实际上,当我们应用-时,什么也没有发生。为了解释为什么,我们可以把这看作是从unsignedunsigned的一种转换.(C17 6.3.1.3):
否则,如果新类型是无符号的,则通过重复地将新类型中可以表示的最大值加或减一,直到值在新类型的范围内,来转换值。
上面的方程被认为是一个没有特定类型的纯数学方程。因此,在您的情况下,我们得到变量value 2147483648unsigned是232-1,然后“再一个”,+1:
-2147483648 -(232-1 + 1)= 2147483648。
我们又回到了开始的地方。事实证明,在unsigned变量的情况下,-没有影响。
long z2 = -z;的情况下,值得注意的是-z子表达式与赋值运算符的左操作数的类型无关。它只是如上所述计算。然后将非负值2147483648赋值给long

  • 如果long是32位,这将是有问题的,因为它不能容纳这么大的值-我们会得到一个实现定义的负数转换(C17 6.3.1.3):

否则,新类型是有符号的,值不能在其中表示;结果是实现定义的,或者引发实现定义的信号。
在一个合理的二进制补码系统上,这很可能导致-2147483648,但是没有任何信号被提出。这是在例如gcc/Windows x86_64(和大多数其他系统)上可能的结果。

  • 如果long是64位的,它可以很好地适合2147483648。这是gcc/Linux x86_64上可能的结果。

对于可移植代码,不要使用longunsigned,而要使用int64_tuint32_t

相关问题