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;
型
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的问题。
z1
z3
-z有什么作用?
-z
[C17一元-运算符的结果是它的(提升的)操作数的负数,整数提升是在操作数上执行的,并且结果具有提升的类型。整数提升只对级别低于int的类型有效,因此在这里不适用。这意味着-z生成unsigned。因此-z生成-2,147,483,648(-231)作为unsigned。
int
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
long
[C17§6.3.1.3 ¶1]当一个整数类型的值被转换为除_Bool之外的另一个整数类型时,如果该值可以用新的类型表示,则该值不变。接下来,我们将2,147,483,648赋值给z2。在您的系统中,long可以表示2,147,483,648,因此赋值给它的值保持不变。这就是我们看到的z2。
_Bool
z2
将231分配给32位long或int
[C17§6.3.1.3 ¶3]否则,新类型是带符号的,值不能在其中表示;结果要么是实现定义的,要么是产生了一个实现定义的信号。接下来,我们将2,147,483,648赋值给z3。这是实现定义的行为。在您的系统上,它将与unsigned(0x80000000)相同的字节模式赋值给int。这就是我们在z3上看到的。
0x80000000
dxxyhpgq2#
一元-运算符的行为如下(C17 6.5.3.3):一元运算符的结果是其(提升)操作数的负数。整数提升是在操作数上执行的,结果具有提升类型。在unsigned的情况下,它不是一个小整数类型(更多信息在这里:Implicit type promotion rules),所以它不受整数提升的影响。实际上,当我们应用-时,什么也没有发生。为了解释为什么,我们可以把这看作是从unsigned到unsigned的一种转换.(C17 6.3.1.3):否则,如果新类型是无符号的,则通过重复地将新类型中可以表示的最大值加或减一,直到值在新类型的范围内,来转换值。上面的方程被认为是一个没有特定类型的纯数学方程。因此,在您的情况下,我们得到变量value 2147483648,unsigned是232-1,然后“再一个”,+1:-2147483648 -(232-1 + 1)= 2147483648。我们又回到了开始的地方。事实证明,在unsigned变量的情况下,-没有影响。在long z2 = -z;的情况下,值得注意的是-z子表达式与赋值运算符的左操作数的类型无关。它只是如上所述计算。然后将非负值2147483648赋值给long。
-
value 2147483648
2147483648
否则,新类型是有符号的,值不能在其中表示;结果是实现定义的,或者引发实现定义的信号。在一个合理的二进制补码系统上,这很可能导致-2147483648,但是没有任何信号被提出。这是在例如gcc/Windows x86_64(和大多数其他系统)上可能的结果。
-2147483648
对于可移植代码,不要使用long或unsigned,而要使用int64_t和uint32_t。
int64_t
uint32_t
2条答案
按热度按时间332nm8kg1#
我将把这个代码片段扩展到以下几个方面:
个字符
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位
long
或int
[C17§6.3.1.3 ¶3]否则,新类型是带符号的,值不能在其中表示;结果要么是实现定义的,要么是产生了一个实现定义的信号。
接下来,我们将2,147,483,648赋值给
z3
。这是实现定义的行为。在您的系统上,它将与unsigned
(0x80000000
)相同的字节模式赋值给int
。这就是我们在z3
上看到的。dxxyhpgq2#
一元
-
运算符的行为如下(C17 6.5.3.3):一元运算符的结果是其(提升)操作数的负数。整数提升是在操作数上执行的,结果具有提升类型。
在
unsigned
的情况下,它不是一个小整数类型(更多信息在这里:Implicit type promotion rules),所以它不受整数提升的影响。实际上,当我们应用
-
时,什么也没有发生。为了解释为什么,我们可以把这看作是从unsigned
到unsigned
的一种转换.(C17 6.3.1.3):否则,如果新类型是无符号的,则通过重复地将新类型中可以表示的最大值加或减一,直到值在新类型的范围内,来转换值。
上面的方程被认为是一个没有特定类型的纯数学方程。因此,在您的情况下,我们得到变量
value 2147483648
,unsigned
是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上可能的结果。对于可移植代码,不要使用
long
或unsigned
,而要使用int64_t
和uint32_t
。