------------------------------------------------------------
author: hjjdebug
date: Tue Jun 10 10:27:23 CST 2014
------------------------------------------------------------
计算机中,-1到底是什么? 他是怎样表示的。
答: -1 是个最大的负整数,0-1=-1, -1在计算机中用补码表示。
-1的表现形式与内存类型相关,通常,-1用32bits 补码表示(0xffffffff),根据上下文,
也可能被变为8bits(0xff), 16bits(0xffff), 64bits
看代码:
#include <stdio.h>
int main(int argc, char *argv[])
{
int a = -1; // 默认的, -1 为4byes 0xffffffff
long int b = -1; // 根据上下文, 内存类型为8bytes, -1 被转换为 0xffffffffffffffff
char c = -1; // 根据上下文, 内存类型为1bytes, -1 被转换为 0xff
short d = -1; // 根据上下文, 内存类型为2bytes, -1 被转换为 0xffff
printf("%d %x\n",-1,-1); // nature
printf("%ld %lx\n",(long int)-1,(long int)-1); // -1 被强制转换为64bits(-1)
// char 数据类型用32bits 表示,不会触发数据变换, 上下文要求传递函数参数,故仍用32bits 表示。
printf("%c %x\n",(char)-1,(char)-1);
printf("size of int: %d\n",sizeof(int));
printf("size of long int: %d\n",sizeof(long int));
return 0;
}
看 gdb 反汇编代码, 加深理解!
(gdb) disassemble /m main
Dump of assembler code for function main:
4 {
0x00000000004004c4 <+0>: push %rbp
0x00000000004004c5 <+1>: mov %rsp,%rbp
0x00000000004004c8 <+4>: sub $0x30,%rsp
0x00000000004004cc <+8>: mov %edi,-0x24(%rbp)
0x00000000004004cf <+11>: mov %rsi,-0x30(%rbp)
5 int a = -1; // 默认的,-1 为4byes 0xffffffff
=> 0x00000000004004d3 <+15>: movl $0xffffffff,-0x14(%rbp)
6 long int b = -1; // 根据上下文, 内存类型为8bytes, -1 被转换为 0xffffffffffffffff
0x00000000004004da <+22>: movq $0xffffffffffffffff,-0x10(%rbp)
7 char c = -1; // 根据上下文, 内存类型为4bytes, -1 被转换为 0xff
0x00000000004004e2 <+30>: movb $0xff,-0x3(%rbp)
8 short d = -1; // 根据上下文, 内存类型为1bytes, -1 被转换为 0xffff
0x00000000004004e6 <+34>: movw $0xffff,-0x2(%rbp)
9 printf("%d %x\n",-1,-1); // nature
0x00000000004004ec <+40>: mov $0x400678,%eax
0x00000000004004f1 <+45>: mov $0xffffffff,%edx
0x00000000004004f6 <+50>: mov $0xffffffff,%esi
---Type <return> to continue, or q <return> to quit---
0x00000000004004fb <+55>: mov %rax,%rdi
0x00000000004004fe <+58>: mov $0x0,%eax
0x0000000000400503 <+63>: callq 0x4003b8 <printf@plt>
10 printf("%ld %lx\n",(long int)-1,(long int)-1); // -1 被强制转换为64bits(-1)
0x0000000000400508 <+68>: mov $0x40067f,%eax
0x000000000040050d <+73>: mov $0xffffffffffffffff,%rdx
0x0000000000400514 <+80>: mov $0xffffffffffffffff,%rsi
0x000000000040051b <+87>: mov %rax,%rdi
0x000000000040051e <+90>: mov $0x0,%eax
0x0000000000400523 <+95>: callq 0x4003b8 <printf@plt>
11 // char 数据类型用32bits 表示,不会触发数据变换, 上下文要求传递函数参数,故仍用32bits 表示。
12 printf("%c %x\n",(char)-1,(char)-1);
0x0000000000400528 <+100>: mov $0x400688,%eax
0x000000000040052d <+105>: mov $0xffffffff,%edx
0x0000000000400532 <+110>: mov $0xffffffff,%esi
0x0000000000400537 <+115>: mov %rax,%rdi
0x000000000040053a <+118>: mov $0x0,%eax
0x000000000040053f <+123>: callq 0x4003b8 <printf@plt>
13 printf("size of int: %d\n",sizeof(int));
0x0000000000400544 <+128>: mov $0x40068f,%eax
0x0000000000400549 <+133>: mov $0x4,%esi
0x000000000040054e <+138>: mov %rax,%rdi
0x0000000000400551 <+141>: mov $0x0,%eax
0x0000000000400556 <+146>: callq 0x4003b8 <printf@plt>
14 printf("size of long int: %d\n",sizeof(long int));
0x000000000040055b <+151>: mov $0x4006a0,%eax
0x0000000000400560 <+156>: mov $0x8,%esi
0x0000000000400565 <+161>: mov %rax,%rdi
0x0000000000400568 <+164>: mov $0x0,%eax
0x000000000040056d <+169>: callq 0x4003b8 <printf@plt>
15 return 0;
0x0000000000400572 <+174>: mov $0x0,%eax
16 }
0x0000000000400577 <+179>: leaveq
0x0000000000400578 <+180>: retq
End of assembler dump.
(gdb)
通俗的解释一下: 谈负数必然要谈到模的概念, 例如8bit的二进制它的模就是256, 钟表它的模就是12, 模就是所有成员的总个数,超过这个数就要取余数降下来.
0xff 我们说它是256, 也可是说它是-1(相对于8bit的模),因为它到模256的距离只差1.
你也可以这么解释, 你看最高位bit7是1,所以是负数, 后面的bit6-bit0是一个补碼表示的数. 那它表示负几呢? 把bit6-bit0取反再加1, 就是1了, 所以是-1, 啊, 好绕啊! 解释都是自圆其说就可以了.
理论就是自圆其说且经得起推理的.
所以重申一下这个事实. 对于客观实在0xff , 你可以说它是255, 也可以说它是-1, 因为你的解释体系不同.
补碼是什么? 补碼就是到模的距离.
二进制的补碼为什么是原碼取反再加1? 因为所有的原碼加上它的反碼就变成全1了,是最大的二进制数, 在加1, 就到了模了.
因为原碼加补碼等于模,现在推出原碼加反碼再加1也等于模,所以补碼就等于反碼加1.