整数的存储
整数的存储分为有符号和无符号的整数的存储,整数2进制的表示方法有三种,分别是原码、反码、补码,内存中存储的是补码,反码可以理解为是一个中转站,原码就是直接将数值按照正负形式翻译成的二进制数字
有符号整形(对于有符号整形中的正数,原反补码一样,同无符号整形一个规则)
对于有符号的整数,对应的二进制最高位表示符号位,其余才表示数值位
原码:数值按照正负形式翻译得到二进制数字
反码:符号位不变,其余按位取反
补码:反码加一
转化规则:原码 -> 补码 和 补码 -> 原码 规则一样都是按位取反,然后加一
无符号整形和正数
原码和反码和补码一样,因为不用考虑符号,存储的都是有效数据,一律都是加法,所以,不用像有符号整型那样,有取反和加一的规则
**整数存储的顺序(大小端模式)**
-
大端:高位存储在低地址,低位存储在高地址
-
小端:高位存储在高地址,低位存储在低地址
对于整形来说:内存中存放的是数据的补码
- 计算机系统中,数值一律用补码来表示和存储,因为使用补码可以将符号位和数值域统一处理
- 符号位的存在使得加法和减法得到了统一处理,(CPU只有加法器),统一转化为加法
- 补码与原码可以相互转化,其运算过程是相同的,不需要而额外电路
整数存储的练习
练习1
//判断大小端
#include <stdio.h>
int check_sys()
{int i = 1;int ret = *((char*)&i);if (1 == ret)return 1;elsereturn 0;
}
int main()
{if (check_sys())printf("小端\n");elseprintf("大端\n");return 0;
}
练习2
#include <stdio.h>
int main()
{char a = -1;signed char b = -1;unsigned char c = -1;printf("a=%d,b=%d,c=%d", a, b, c);}
解释
#include <stdio.h>
int main()
{char a = -1;//原码 -1: 10000000 00000000 00000000 00000001//反码 -1: 11111111 11111111 11111111 11111110//补码 -1: 11111111 11111111 11111111 11111111//截断a: 11111111signed char b = -1;//截断a: 11111111 unsigned char c = -1;//截断a: 11111111printf("a=%d,b=%d,c=%d", a, b, c);//输出a 整形提升//补码 11111111 高位补符号位//补码 11111111 11111111 11111111 11111111//反码 11111111 11111111 11111111 11111110//原码 10000000 00000000 00000000 00000001 -1//输出b 整形提升// b同a -1//输出c 整形提升//补码 11111111 高位补0(无符号整形)//补码 00000000 00000000 00000000 11111111 (正数)//反码 00000000 00000000 00000000 11111111//原码 00000000 00000000 00000000 11111111 255return 0;
}
练习3
#include <stdio.h>
int main()
{char a = -128;//原 -128: 10000000 00000000 00000000 10000000//反 -128: 11111111 11111111 11111111 01111111//补 -128: 11111111 11111111 11111111 10000000//截断 a: 10000000printf("%u\n", a);//补码: 11111111 11111111 11111111 10000000 (整形提升)//反码: 11111111 11111111 11111111 10000000//原码: 11111111 11111111 11111111 10000000return 0;
}
练习4
//练习4
#include <stdio.h>
#include <string.h>
int main()
{char a[1000];int i = 0;for (i = 0; i < 1000; i++){a[i] = -1 - i;}printf("%zd", strlen(a));//数组a存储的是-1到-1000吗 还是什么//a的长度是多少//char 的范围为-128到127//-1 -2 ... -128 127 ... 2 1 0// 255return 0;
}
练习5
#include <stdio.h>
int main()
{unsigned char i = 0;for (i = 0; i <= 255; i++){printf("hello world\n");}//死循环//unsigned char 0-255 不会超过255return 0;
}
#include <stdio.h>
int main()
{unsigned int i;for (i = 9; i >= 0; i--){printf("%u\n", i);}//死循环//unsigned int 不会小于0//一直输出return 0;
}
练习6
#include <stdio.h>
//x86 小端模式
int main()
{int a[4] = { 1,2,3,4 };int* ptr2 = (int*)((int)a + 1);printf("%x", *ptr2);//%x打印16进制//ptr2//数组a的地址转化为int类型,//01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00return 0;
}
浮点数的存储
浮点数的存储不同于整数存储,没有原码反码补码概念,他的存储是按照国际标准IEEE754,即任意一个二进制浮点数V都可以表示为如下形式
V = (-1)^ S * M * 2 ^ E
-
(-1) ^ S 表示符号位,当S=0,V为整数;当S=1,V为负数
-
M表示有效数字,M>=1&&M<2,使用科学计数法
-
2^E表示指数位
举例
IEEE754规定:
对于32位的浮点数,最高位存储符号位S,接着8位存储指数E,剩下的23位存储有效数字M
对于64位的浮点数,最高位存储符号位S,接着11位存储指数E,剩下的52位存储有效数字M
浮点数存储过程
IEEE754 对有效数字M和E,有一些特别规定
M>=1&&M<2,也就是说,M可以写成1.xxxxxx的形式,其中xxxxxx表示小数部分,但在计算机内部保存时,IEEE754规定:默认M的第一个数为1,舍去存储,只保存小数部分。比如保存1.01时,只保存01,等到读取的时候,再把第一位的1加上去。这样做的目的是,节省一位有效数字。
对于指数E要分情况考虑(E是一个无符号整数)
如果E为8位,他的表示范围就是0-255,如果E为11位的话,表示范围就是0-2047。但是,由于科学及算法可以出现负数,这与E的定义有矛盾,所以IEEE754就规定,存入内存时E的真实值必须加上一个中间数,对于8位E,中间数为127,对于11位E,中间数为023。比如,2^10的E是10,保存为32位浮点数是,内存中必须存储为10+127=137,即10001001。
浮点数读取过程
指数E从内存中读取还可以分为三种情况
E不全为0或不全为1(正常情况)
由于上面内存中存储E是加上了中间值,因此读取时会减掉中间值,得到真实值,再将有效数字M加上1。
比如:0.5的二进制形式为0.1,由于规定整数部分必须为1,所以小数点要向右移一位,即为1.0^2(-1),其内存中的E为-1+127(中间值),表示为01111110,而M去掉正数部分为0,补齐0到23位
00000000000000000000000
E全为0
浮点数的指数E为-127(-中间值),内存中才会存储为全是0,这是读取有效数字M不再加上1,还原为0.xxxxxx的小数。这样做是为了表示无限接近0,以及接近于0的很小数字。
E全为1
这时内存中的E全为1,如果是32位浮点数,E则为128,2^128,这时尾数部分如果为0,则表示无限大。
## 整数和浮点数的存储规则理解就可以,尤其是对于浮点数来说注重理解,千万不要死记硬背