大家好,上次学习还是5月31,从今天开始,暑假学习正式开始喽!快一起叭。
这次的内容是数据在内存中的存储。
仍然是点赞+关注,追番不迷路,谢谢大家!
文章目录
- [1. 整数在内存中的存储](#1. 整数在内存中的存储)
-
- (1)整形存储的是二进制的补码
- [(2) 大小端字节序和字节序判断](#(2) 大小端字节序和字节序判断)
- (3)练习
- 2.浮点数在内存中的存储
1. 整数在内存中的存储
(1)整形存储的是二进制的补码
这部分的内容我们在之前已经讲解过了,具体内容在链接
整数有原码,反码,补码这三种2进制的表达形式。但是内存中存放的是补码。计算使用的是内存中的二进制,所以计算使用的就是补码。
有符号的整数,三种表示方法均有(符号位,数值位)两部分,符号位是最高位的一位:且0表示正,1表示负。其余的31位均是数值位。
- 正整数的原反补均相同。
- 负整数的原反补均不一样。
- 原码:直接将数值 按照正负数的形式翻译成二进制得到的就是原码。
- 反码:原码的符号位不变,其余位按位取反(~)
- 补码:反码+1
(2) 大小端字节序和字节序判断
整数在内存中存储时非常重要的一个事情
为什么是'字节序'?因为是以字节为单位来讨论它们的顺序。
地址:低--------------------->高(从左到右,低地址-->高地址)
内容:高--------------------->低(从左到右,万千百十,高字节内容-->低字节内容)
大端字节序(正着放):将一个数据的低位字节内容 存放在内存的高地址处
小端字节序(倒着放):将一个数据的高位字节内容 存放在内存的高地址处
上图展示的就是小端字节序。
(3)练习
练习一:请简述大端字节序和小端字节序的概念,设计一个小程序来判断当前机器的字节序。
----思绪
【从简单开始,设a的值是1(a是int类型,占4个字节),它的十进制是0x00 00 00 01。如果是大端字节序(01 00 00 00,小端(00 00 00 01),只需要去拿四个字节中的第一个字节,如果是大端字节序存储,则第一个字节是1,小端则第一个字节是0。现在需要想办法拿到int类型数字的第一个字节----拿到首个字节的地址,强制类型转换成char*,之后再解引用,即可拿到第一个字节的内容】
c
int main()
{
int a = 1;
if (*(char*)&a == 1)
printf("小端字节序储存\n");
else
printf("大端字节序储存\n");
return 0;
}
练习二
c
int main()
{
signed char a = -1;
unsigned char b = -1;
printf("a=%d,b=%d", a, b);
//%d打印有符号整数,%u是打印无符号整数
return 0;
}
-1的原码:10000000000000000000000000000001
-1的反码:1111111111111111111111111111111111110
-1的补码:1111111111111111111111111111111111111
以上写的是int类型,4个字节,4*8=32个比特位。
但是a和b是char类型,8个比特位(现在取int类型低位的8个比特位)所以a(char类型)里面放的是11111111,%d(整数32比特位)打印它的时候,需要整型提升,提升的时候需要看a是什么类型,signed提升(补符号位 ,即第一个)之后是11111...(32位)之后取反加一得数值-1。无符号unsigned提升,其余高位补0,即000...11111111,开头是0,正数,反即原,即255
此处再附送一道练习题:
c
int main()
{
char a = -128;
printf("%u", a);//打印无符号整数,11...100000000,说它是无符号-->正数,补码即原码
printf("%d", a);//打印有符号整数,开头是1,负数,取反加一
return 0;
}
练习三
c
int main()
{
char a[1000];
int i;
for (i = 0; i < 1000; i++)
{
a[i] = -1 - i;
}
printf("%d", strlen(a));
return 0;
}
//结果是255
此处需要注意的是,char类型的取值范围是:-128~127。此处a=128,存不下。unsigned char是0 ~255。【signed认为第一个是符号位,unsigned认为不是】
i从0开始加,加到127,再加1,则变为-128,之后继续-1,则-127,-126,-125...-1,0。在遇到0之后,strlen便不在继续统计(strlen统计\0之前的字符个数)
练习题
c
#include<stdio.h>
unsigned char i = 0;
int main()
{
for (i = 0; i <= 255; i++)
{
printf("hello world");
}
}
在此处,i的范围是0~255,一直符合条件,for陷入死循环。
练习四
c
#include <stdio.h>
//X86环境 ⼩端字节序
int main()
{
int a[4] = { 1, 2, 3, 4 };
int *ptr1 = (int *)(&a + 1);
int *ptr2 = (int *)((int)a + 1);
printf("%x,%x", ptr1[-1], *ptr2);
return 0;
}
&a的类型是int* [4],在题目中将其强制类型转换成int*类型(&a+1即ptr1亦然)
a是整个数组的地址,(int)a是将地址强制类型转换成int类型。假设a的地址是0x10,(int)a+1即0x10+1(16+1),即0x11(17)【两者之间差一个字节】,再将0x11又转换成int*类型赋给ptr2
2.浮点数在内存中的存储
1.浮点数类型
浮点数包括的类型:float,double,long double
2. 浮点数公式
(1) 根据国际标准IEEE754,任意一个二进制浮点数V可以表示成下面的形式:
V = (−1) 的S次方 M ∗ 2的E次方
- S 表示符号位,当S=0,V为正数;当S=1,V为负数
- M 表示有效数字,1<=M<2
- E 表示指数位
(2) 浮点数的存储,存储的就是S.M.E相关的值。
(3) IEEE 754规定:
对于32位的浮点数(float),最高的1位存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M
对于64位的浮点数(double),最高的1位存储符号位S,接着的11位存储指数E,剩下的52位存储有效数字M
3.有效数字M
对于有效数字M,有特别的规定:在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分。【原因:比如保存1.01的时候,只保存01,等到读取的时候,再把第一位的1加上去。这样做的目的是:节省1位有效数字。以32位浮点数为例,留给M只有23位,将第一位的1舍去以后,等于可以保存24位有效数字。】
4.指数E
指数E的情况较为复杂,首先,E是无符号整数(unsigned int),如果E是8位,取值范围是255;如果是11位,取值范围是1047。
但是,科学计数法中,指数是可以是负数的。比如:十进制的0.5,即二进制0.1,即
所以,IEE 754规定:存入内存时,E的真实值必须再加上中间数(对于8位的E,中间数是127;对于11位的E,中间数是1023)。假如E的值是10,存入的是10+127=137,即10001001。(这个行为可以让E变为正数)
IEE 754规定:指数E从内存中取出分为3种情况:
(1)正常情况下(即不全不是0或不全是1):E-127或1023得到真实值,再在有效数字M前面加上1。
(2)E全为0:这时,浮点数的指数(即真实的E)是-127或者-1023,1.xxxxx乘2的-127次方,这个数字无限接近于0 。==真实值E=1-127=-126,且有效值M不再加上第一位1。==还原位0.xxxxx的小数。
(3)E全为1:这时,浮点数的指数(即真实的E)是128或者,1.xxxxx乘2的128次方,这个数字接近于无穷大。
- 以整数的形式放进去,再以整数的形式取出来---->按整数的视角来看,原反补。int n = 9; --------printf("n的值为:%d\n", n);【答案:9】
- 以浮点数的形式放进去,以浮点数的形式取出来--->S,M,E相关的东西。*pFloat = 9.0;------- printf("*pFloat的值为:%f\n", *pFloat);【答案:9.0】
- 以整数的形式放进去,再以浮点数的形式取出来--->整数放进到二进制序列 当成 浮点数的序列往出返,解析。int n = 9;-----printf("*pFloat的值为:%f\n", *pFloat);
- 以浮点数的形式放进去,以整数的形式读取--->先将它弄成二进制序列,当成补码。*pFloat = 9.0;-----printf("num的值为:%d\n", n);
5.练习题
c
#include <stdio.h>
int main()
{
int n = 9;
float* pFloat = (float*)&n;
printf("*pFloat的值为:%f\n", *pFloat);
*pFloat = 9.0;
printf("num的值为:%d\n", n);
return 0;
}
1. 第一部分:整------>浮点数
c
int n = 9;
float* pFloat = (float*)&n;
printf("*pFloat的值为:%f\n", *pFloat);//%f只能打印小数点后6位
第一步:先将n的二进制写出来:00000000000000000000000000001001
0 00000000 00000000000000000001001
S是0 E M
}
因为E全是0,所以E=-126,且还原为小数0.00000000000000000001001
(-1)^0 * 0.00000000000000000001001 * 2^-126
,这个数接近于0,只打印6位,即0.000000。
2. 第二部分:浮点---->整数
c
*pFloat = 9.0;
printf("num的值为:%d\n", n);
9.0------>1001.0---->1.0010 * 2^3
正数S=0;有效数M是0010:指数E的真实值是3(存进去的是127+3=130,130是10000010),写成二进制形式
0 10000010 00000000000000010转换成整数即可1091567616