文章目录
数据在内存中的存储
整数在内存中的存储
整数在内存中是以补码的形式存储的
整数的二进制表示有三种:原码、反码、补码
对于有符号整数,它的最高位视为符号位,1表示负,0表示正。
正整数的原码、反码、补码相同。
负整数的原码、反码、补码不同:
原码:将原数直接写成二进制表示的形式
反码:符号位不变,其余各位取反
补码:反码加一
补码转化为原码:1.补码建1,再取反(符号位不变)2.补码取反(符号位不变),再加1,与原码转化为补码的过程一致。
为什么计算机存储的是补码呢?
在计算机系统中,数值⼀律⽤补码来表⽰和存储。
原因在于,使⽤补码,可以将符号位和数值域统⼀处理; 同时,加法和减法也可以统⼀处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。
大小端字节序存储
大小端
超过一个字节的数据在内存中存储时会出现存储顺序的问题,基于此,我们按照不同的存储顺序分为大端字节序存储和小端字节序存储。
大端字节序存储
是指数据的低位字节内容保存在内存的⾼地址处,⽽数据的⾼位字节内容,保存在内存的低地址处。
小端字节序存储
是指数据的低位字节内容保存在内存的低地址处,⽽数据的⾼位字节内容,保存在内存的⾼地址处。
- 以VS为例:
首先,VS调试的内存窗口是以十六进制表示的,这么做只是为了方便展示,并不能说明内存中是如此存储的。
129的二进制表示为:00000000 00000000 00000000 10000001
将上面的二进制序列化为十六进制表示:00 00 00 81
对于这个十六进制序列,81相对00就是低位,我们联想一下十进制的54,4是个位,就是低位。
而我们观察第二张图,低位的81存储在了较低地址处,按照上面介绍的大小端字节序存储,我们可以得出结论VS采用小端字节序存储。
了解到这里,我们尝试应对百度的一道题目:
写一个程序,判断当前机器是大端还是小端:
//1.指针
int check_sys()
{
int i = 1;
return *((char*)&i);
}
int main()
{
int ret = check_sys();
if (1 == ret)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}
//思路就是,我们创建一个整型类型,赋值为1,1的二进制表示为00000000 00000000 00000000 00000001
//取变量的地址,将它强制类型转换为char*类型,然后解引用,char*类型解引用访问一个字节。
//如果结果是0,证明当前机器存储1的顺序是00000000 00000000 00000000 00000001,为大端字节序存储。
//如果结果是1,证明当前机器存储1的顺序是00000001 00000000 00000000 00000000,为小端字节序存储。
//联合
int chack_sys()
{
union
{
int i;
char c;
}un;
un.i = 1;
return un.c;
}
int main()
{
int ret = check_sys();
if (1 == ret)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}
我们对i赋值1,会改变c的值
如果是小端字节序存储:c的位置存储的是00000001
如果是大端字节序存储:c的位置存储的是00000000
如此我们便判断出当前机器的大小端。
我们知道,char类型其实也是整型家族的一员,char也有无符号char和有符号char,char究竟默认为有符号char还是无符号char,这是不确定的,取决于编译器。不过,VS的char默认就是有符号char
这段代码的输出结果是什么?
#include <stdio.h>
int main()
{
char a = -1;//标号1
signed char = -1;//标号2
unsigned char = -1;//标号3
printf("a=%d,b=%d,c=%d",a,b,c);
return 0;
}
VS2022环境下,输出的结果是
为什么会出现这种情况?
观察题目,标号1和标号2的语句其实是一样的,因为VS2022下的char默认为有符号char。
首先写出-1在内存中存储的补码:11111111 11111111 11111111 11111111
a变量无法存储32个bit位,发生截断,截取最低位起8个bit位:11111111,这就是a变量在内存中存储的二进制序列
我们以%d(有符号整数)的形式打印,首先会整型提升,整型提升有两种情况,char是有符号的,所以整型提升时补符号位,这里是1,得到:11111111 11111111 11111111 11111111
以有符号整型打印,计算机认为整型提升后的二进制序列是一个有符号整数的补码,最高位是1,为负数,原码、反码、补码不同,打印时需要转化为原码:10000000 00000000 00000000 00000001,转化过来,就是-1,所以打印a、b的结果都是-1。
对于c我们采取同样的步骤分析:
-1的补码为:11111111 11111111 11111111 11111111,存储在unsigned char中会发生截断,存储的是:11111111
以%d形式打印,发生整型提升,此时c是无符号char类型,整型提升时补0,得到:00000000 00000000 00000000 11111111
最高位为0,是正数,原码、反码、补码相同,所以整型提升后的二进制序列就是原码,就是255,所以打印255。
我们再看一道题目:
#include <stdio.h>
unsigned char i = 0;
int main()
{
for(i = 0; i <= 255; i++)
{
printf("hello world\n");
}
return 0;
}
打印结果是什么?
答案是:死循环,一直打印hello world。
unsigned char类型的取值范围是0~255
当i = 255时,其在内存中的存储是11111111
当第256次循环时,i 理应是256,但是不是这样的:
256的二进制表示为:00000000 00000000 00000001 00000000
存储在i中,会发生截断只取8个bit位00000000,i的值其实是0,然后再次到255,如此构成死循环,也就是说i <= 255这个条件恒成立。
浮点数在内存中的存储
浮点数在内存中的存储不同于整数在内存中的存储。
根据国际标准IEEE(电⽓和电⼦⼯程协会) 754,任意⼀个⼆进制浮点数V可以表⽰成下⾯的形式:
V = (-1)^S * M * 2^E
- **(-1)^S: ** 为符号位,S为0表示正数;S为1表示负数。
- **M: ** 表示有效数字,它的大小大于1,小于2.
- 2^E: 表示指数位
对于上述知识,我们举个例子来解释:
十进制表示的浮点数5.5
二进制表示:101.1 (小数点后面的第一位权重为2^-1)
我们将它写成由S、M、E表示的形式:(-1)^0 * 1.011 * 2^2
S;0
M:1.011
E:2
我们发现,如果我们知道任意一个浮点数的上述表示形式的S、M、E,我们就能将这个浮点数还原成我们常见的十进制表示形式。
其实,浮点数在内存中存储的就是S、M、E的值。
对于32位的浮点数(float),它的最高位存储S,8个bit位存储E,23个bit位存储M
对于64位的浮点数(double),它的最高位存储S,11个bit位存储E,52个bit位存储M
存
首先,最高位存的S很简单,是负数就存1,是正数就存0。
我们前面举了十进制5.5的例子,M为1.011,存储M时其实并不会存小数点前的1,23个位存储的是小数点后面的数011,这样做的好处是:能够多存储一位,增加了浮点数存储的精确度。
标准规定E是一个无符号整数
我们清楚,科学计数法的指数可能为负数。针对这种情况,引入了一个中间值(32位浮点数为127,64位浮点数为1023),规定在存储E时,要先加上这个中间值,比如某个32位浮点数的E为3,那么真实存储的为127 + 3 = 130,如果E为-1,那么真实存储的是-1 + 127 = 126。标准规定的这个中间值是很巧妙的,保证不会出现某个指数E加上中间值为负数的情况,这一点我们不必担心。
取
S的取很简单,我们不多赘述。
我们在取的时候大致分为三种情况:
-
E不全为0且E不全为1
将存储的E拿出来后,减去中间值,得到E的值,然后拿出M的值,并在前面加上1.
-
E全为0
我们想,我们存储E时要加上一个中间值,而加上了这个中间值,E在计算机种存储的仍然全为0,所以我们知道,这会是一个非常小的数。对于这个非常小的数,取的时候E默认为1 - 127,同时取出M时,不再会加上1,而是以0点几的形式拿出来。
-
E全为1
这种情况一定是一个很巨大的数,这个数取出来后是无穷大,符号由S决定。
我们举个例子:
例如我们存十进制的5.5
二进制表示:101.1
转化为S、M、E的形式:(-1)^S * 1.011 * 2^2
我们假设这个数是float类型的
那么它存储在计算机的二进制序列为:
0 10000010 01100000000000000000000
解析
存:
正数,所以S是0
E是2,32位浮点数存储E时会加上中间值127,所以存储在计算机内部的其实是127 + 2 = 129这个数字
M存储时,不存小数点前面的1,只存储小数点后面的011,不够的补0,所以M存储为01100000000000000000000
取:
S取出来是0,所以是正数
E取出来是129,减去中间值127 ,得到2
由于E的存储不全为0且不全为1,所以M取出来后加上1,得到1.011
再根据取出来的S、M、E就能还原出十进制32位浮点数5.5。
标签:存储,00000000,浮点数,补码,char,内存,数据 From: https://blog.csdn.net/xiaokuer_/article/details/136864406