Is this undefined behavior?
It prints -128
as the result:
#include<stdio.h>
int main()
{
char i=-128;
i=-i;
printf("%d",i);
}
Please explain.
Is this undefined behavior?
It prints -128
as the result:
#include<stdio.h>
int main()
{
char i=-128;
i=-i;
printf("%d",i);
}
Please explain.
8 位有符号值中 -128 的二进制补码是 -128。
查看二进制值:
原值:10000000
补码:01111111
增量:10000000
这不是未定义的行为。假设 typechar
在您的平台上签名,它是实现定义的行为。(C99,6.3.1.3p3)
i = -i
;
i
in-i
首先被提升为,然后int
被整数转换转换为-i
。128
128
char
这是标准的段落,它说 to 的转换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
。
-128 = 0b10000000
所以你想找到:-(-128):
~(-128) = 0b01111111
0b01111111 + 0b1 = 0b10000000 = -128
因此,对于 8 位数字,-(-128) = -128!
您正在使用签名的字符。有符号正单字节的最大值为 127。通过否定 -128 以尝试达到 +128,您最终会溢出到 -128。这种行为很可能取决于操作系统和编译器。
这不是未定义的行为。
乘以 -1 相当于取反位然后加 1。因此,你真的在做,
~10000000+1 = 01111111+1 = 10000000
这仍然是-128
。
我得到相同的结果。
这是程序集(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
注意二进制值。
记住你的“二进制补码”算术。
是否有意义?
====== 附录 ======
这真的,真的很简单。这是一个稍作修改的示例,可能有助于澄清:
#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