计算机中的整数共有3种二进制的表示方法:原码、反码、补码。下面是对这3种表示方法的介绍:
1.计算机中的整数共有3种二进制表示方法,即:原码、反码、补码(内存中存的就是补码)
2.三种方法均有符号位(即最左边的第一个数字)和数值位两部分,符号位用“0”表示“正”,“1”表示“负”
3.正数的原、反、补码都相同;而负数则非也。
代码示例如下:
//正数的原码、反码、补码和负数的原码、反码、补码 int main() { int a = 20;//正数的 //int-->4byte-->32bit; //00000000000000000000000000010100-->20的原码 //00000000000000000000000000010100-->20的反码 //00000000000000000000000000010100-->20的补码 int b = -10;//负数的 //10000000000000000000000000001010-->-10的原码 //11111111111111111111111111110101-->-10的反码(将原码中的“1”变成“0”,“0”变成“1”即可得到负数的反码) //11111111111111111111111111110110-->-10的补码(将反码加“1”即可得到负数的补码) //1111(f)1111(f)1111(f)1111(f)1111(f)1111(f)1111(f)0110(1*2^2+1*2^1=6)(写成16进制) //即f f f f f f f 6(倒置存放:f 6 f f f f f f) return 0; }
4.在计算机中,数字一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理
5.同时,加法和减法也可以统一处理(CPU只有加法器),此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路
6.内存中存放的是补码,整型表达式计算使用的是内存中的补码来计算的
7.打印和我们看到的都是原码
代码示例如下:
//int c = 1 - 1; //1-1会转化成1+(-1)计算 // //转换成原码计算 //00000000000000000000000000000001-->1的原码 //10000000000000000000000000000001-->-1的原码 //10000000000000000000000000000010-->相加之后---->-2 error //转换成补码计算 // 00000000000000000000000000000001-->1的补码 // 11111111111111111111111111111111-->-1的补码 //100000000000000000000000000000000-->进一以后最左边的那个“1”没用了,会丢失,最后会得到如下所示补码 // 00000000000000000000000000000000-->0=1-1 right
注意:
(1)原码到补码--->1种方式(即为上面代码通过原码求补码的方法)
(2)补码到原码--->2种方式---->1.先”减一“(得反码),再取反(原码) 2.先取反,再“加一”(注意是加一)
8.大小端
(1)出现原因:一个数值超过一个字节了,要存储到内存中就有顺序问题
(2)大端(存储)模式,是指数据的低位保存在内存的高地址中,而数据的高位保存在内存的低地址中;
(3)小端(存储)模式,是指数据的高位保存在内存的高地址中,而数据的低位保存在内存的低地址中;(VS、x86)
(4)小端字节序存储:把一个数值的低位字节的内容,存放在低地址处,高位字节的内容,存放在高地址处
(5)大端字节序存储:把一个数值的低位字节的内容,存放在高地址处,高位字节的内容,存放在低地址处
解释如图:
设计一个程序来判断当前机器的字节序:
代码如下:
//在main函数中实现 #include <stdio.h> int main() { int a = 1; char* p = (char*)&a; if (*p == 1) printf("小端\n"); else printf("大端\n"); return 0; } //调用函数简化版 #include <stdio.h> int check_sys() { int a = 1; return (*(char*)&a == 1);//不要忘记了解引用 } int main() { if (check_sys() == 1) printf("小端\n"); else printf("大端\n"); return 0; }
练习1:
结果如下:
解释如下:
//整型提升的时候要看有无符号 //有符号--->高位(即符号位)是什么就补什么 //无符号--->补0 #include <stdio.h> int main() { char a = -1;//-1截断以后存到a中 //10000000000000000000000000000001-->-1的原码(-1是整数,int类型有32个bit) //11111111111111111111111111111110-->-1的反码 //11111111111111111111111111111111-->-1的补码(a是char类型的变量,占一个字节,只能存8个比特位,所以只能存低地址的8个比特位,如下(被截断)) //11111111--->最高位是符号位 //a--->整型提升以后如下 //11111111111111111111111111111111--->补码 //10000000000000000000000000000001--->原码-------->-1 signed char b = -1;//(同上) //10000000000000000000000000000001-->-1的原码(-1是整数,int类型有32个bit) //11111111111111111111111111111110-->-1的反码 //11111111111111111111111111111111-->-1的补码(a是char类型的变量,占一个字节,只能存8个比特位,所以只能存低的8个比特位,如下(被截断)) //有符号--->最高位是符号位 //11111111--->最高位是符号位 //a--->整型提升以后如下 //11111111111111111111111111111111--->补码 //10000000000000000000000000000001--->原码-------->-1 unsigned char c = -1; //11111111111111111111111111111111-->-1的补码 //11111111 //无符号--->不会将最高位看作符号位 //c--->整型提升以后如下 //00000000000000000000000011111111(高位补0) //提升以后,最高位是0,表明是正数,故 //原码,反码,补码都一样 //所以打印出来是255 printf("a=%d,b=%d,c=%d", a, b, c);//%d--->打印整型 return 0; }
练习2:
结果如下:
解释如下:
//下面程序输出什么 #include <stdio.h> int main() { char a = -128; //10000000000000000000000010000000--->-128的原码 //11111111111111111111111101111111--->-128的反码 //11111111111111111111111110000000--->-128的补码 //10000000--->整型提升 //11111111111111111111111110000000---("%u\n",a) printf("%u\n", a);//%u打印无符号整型,认为内存中存放的补码是一个无符号数;%d打印有符号整型,认为内存中存放的补码是一个有符号数 return 0; }
若将a改为128也是一样的答案:
解释如下:
#include <stdio.h> int main() { char a = 128;//即使成了128,答案还是不变 //00000000000000000000000010000000--->-128的原码 //01111111111111111111111101111111--->-128的反码 //01111111111111111111111110000000--->-128的补码 //10000000--->整型提升 //11111111111111111111111110000000---("%u\n",a) printf("%u\n", a);//%u打印无符号整型,认为内存中存放的补码是一个无符号数;%d打印有符号整型,认为内存中存放的补码是一个有符号数 return 0; }
练习3:
结果如下:
解释如下:
#include <stdio.h> int main() { int i = -20; //10000000000000000000000000010100--->-20的原码 //11111111111111111111111111101011--->-20的反码 //11111111111111111111111111101100--->-20的补码 unsigned int j = 10; //00000000000000000000000000001010--->10的原码(补码) printf("%d\n", i + j); //补码相加 //11111111111111111111111111101100--->-20的补码 //00000000000000000000000000001010--->10的原码(补码) //11111111111111111111111111110110--->相加 //11111111111111111111111111110101--->反码 //10000000000000000000000000001010--->原码---->-10 return 0; }