这是未定义的行为吗?
它打印-128
作为结果:
#include<stdio.h>
int main()
{
char i=-128;
i=-i;
printf("%d",i);
}
请解释。
答案 0 :(得分:8)
8位有符号值中的-128的二进制补码是-128。
查看二进制值:
原值:10000000
补充:01111111
增量:10000000
答案 1 :(得分:4)
这不是未定义的行为。假设在您的平台中签署了char
类型,它是实现定义的行为。 (C99,6.3.1.3p3)
i = -i
;
i
中的-i
首次提升为int
,因此-i
为128
,然后128
转换为char
通过整数转换。
以下是标准的段落,其中说128
到char
的转换是实现定义的:
(C99,6.3.1.3p3)否则,新类型已签名且值不可 代表其中;结果是实现定义的,或者引发实现定义的信号。
编辑:虽然它是实现定义的,但大多数实现之间存在共同的实现行为。以下是gcc
(以及大多数其他编译器所做的)文档:
为了转换为宽度为N的类型,将该值减去模2 ^ N以在该类型的范围内;没有信号被提出
http://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html
请注意,并非所有编译器都以这种方式运行。一些(DSP)编译器只是饱和。在这种情况下(仍然假设已签名char
),在i = -i;
之后,i
的值将为127
。
答案 2 :(得分:1)
-128 = 0b10000000
所以你想找到: - ( - 128):
~(-128) = 0b01111111
0b01111111 + 0b1 = 0b10000000 = -128
所以使用8位数字, - ( - 128)= -128!
答案 3 :(得分:0)
您正在使用已签名的字符。有符号正单字节的最大值为127.通过否定-128尝试到达+128,最终溢出到-128。这种行为可能与操作系统和编译器有关。
答案 4 :(得分:0)
这不是未定义的行为。
乘以-1等于否定位然后加1.这样,你真的在做,
~10000000+1 = 01111111+1 = 10000000
仍为-128
。
答案 5 :(得分:0)
我得到了相同的结果。
这是程序集(Microsoft Visual Studio):
; File tmp.c
; Line 5
push ebp
mov ebp, esp
push ecx
; Line 6
mov BYTE PTR _i$[ebp], -128 ; ffffff80H
; Line 7
movsx eax, BYTE PTR _i$[ebp]
neg eax
mov BYTE PTR _i$[ebp], al
; Line 8
movsx ecx, BYTE PTR _i$[ebp]
push ecx
push OFFSET FLAT:$SG775
call _printf
add esp, 8
; Line 9
xor eax, eax
; Line 10
mov esp, ebp
pop ebp
ret 0
_main ENDP
请注意二进制值。
记住你的“二次补码”算法。
有意义吗?
====== ADDENDUM ======
这真的很简单。这是一个稍微修改过的示例,可能有助于澄清:
#include <stdio.h>
int
main (int argc, char *argv[])
{
int i=-128, j, k, l;
char c = i;
printf("i: %d == 0x%x; c: %d == 0x%x\n", i, i, c,c);
j=-i;
k=~i;
l=0-i;
i=-i;
c=-c;
printf("%d, %d, %d, %d, %d\n",i,j,k,l,c);
printf("0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n",i,j,k,l,c);
return 0;
}
结果:
i: -128 == 0xffffff80; c: -128 == 0xffffff80
128, 128, 127, 128, -128
0x80, 0x80, 0x7f, 0x80, 0xffffff80