数据在内存中的存储
前言
本文会详细介绍数据在内存中的存储,包括整型,字符型及浮点型
一、数据类型介绍
1、数据类型的基本分类
1.1、整型分类
字符型:char unsigned char
短整型:short [int] unsigned short [int]
整型: int unsigned int
长整型:long [int] unsigned long [int]
1.2、浮点型分类
单精度浮点型 : float
双精度浮点型 : double
二、数据在内存中的存储
1、整型在内存中的存储
1.1 原码、反码、补码
整型分为符号位和数值位,符号位为0表示正数,为1表示负数。
对于正数来说,原码、反码、补码均相同,求出原码即可
对于负数来说:
原码:直接将数值按照正负形式转为二进制就是原码
反码: 符号位不变,其他位置数按位取反
补码: 反码+1得到补码
对于整型来说,数据在内存中存储的是补码
1.2求两个整型数据在内存中存储的形式
#include<stdio.h>
int main()
{
int a = 5;
//00000000000000000000000000000101 //补码
//00 00 00 05
int b = -10;
//10000000000000000000000000001010 //原码
//111111111111111111111111111111110101 //补码
//111111111111111111111111111111110110 //补码
//ff ff ff f6
return 0;
}
在内存中,会如何存放a和b呢?
可以发现,内存中是倒着存的,为什么呢?接下来介绍大小端字节序。
2、大小端字节序
2.1大端存储和小端存储
大端存储:是指数据的低位保存在内存的高地址中,数据的高位保存在内存的低地址中
小端存储:是指数据的低位保存在内存的低地址中,数据的高位保存在内存的高地址中
上述代码是大端存储还是小端呢?显而易见,是小端存储,在这里我用一张图来说明:
3、一道有趣的题
#include<stdio.h>
int main()
{
char a = -1;
//11111111
//整型提升后(因为是有符号数 第一位是1 补1)
//11111111111111111111111111111111 --补码
//10000000000000000000000000000000 --取反
//10000000000000000000000000000001 --原码
//则最后会输出-1
signed char b = -1;
//11111111
//和 a 是一样的
unsigned char c =-1;
//11111111
//整型提升后(因为是无符号数,补0)
//00000000000000000000000011111111 -255
printf("a=%d,b=%d,c=%d",a,b,c);// a=-1 b=-1 c=255
return 0;
}
先分析以下代码,三个变量a ,b ,c 分别是字符型,有符号字符型和无符号字符型,在上文提到说,说数据在内存中存放的是补码,可以分析得出,-1的补码是11111111,那么,a,b,c在内存中存储的是ff。最后,要按照%d类型进行打印,在这里介绍一下%d,%d是以十进制打印有符号的整型,这个位置是字符型,就要进行整型提升(简单来说就是为了获得整型的精度,会将字符型和短整型提升为整型,参与运算。整型提升是在运算前就已经完成了。)如何提升呢?整型提升是按照变量数据类型的符号位来提升的,如果符号位是1,则补1,如果符号位是0,则补0,无符号数补0。
输出结果如下:
三、浮点型在内存中的存储
1、浮点型在内存中的存储规则
根据国际标准,任意进制的浮点数V可以表现为以下形式:
V=(-1)^S * M * 2^E
(-1)^S :表示符号位,S=0,表示正数,S=1,表示负数
M:表示有效数字,取值范围是大于等于1,小于2
2^E:表示指数位
例如:5.5十进制,用二进制表示就是101.1
用上面的存储规则就是这么表示:
5.5 = (-1)^ 0 * 1.011*2^2
2、float 和 double
本文着重介绍float。float是单精度浮点型,会向内存申请四个字节的空间来存储浮点数。如何存储?用一个简单的图来说明:
S就是表示0或者1,不细说,主要来介绍一下M和E
M:
国际规定,在标准中,默认第一位总是1,因此会把第一位舍去,保留小数点后面的,例如保存1.01,就只会保存01,当取出时,会自动在小数点前补1。
因此,舍去了第一位后,就可以保存24位有效数字。
E:
首先,E是一个无符号的整数,取值范围是0-255,有一种情况是这样,比方想表示0.5 ,转换为2进制是 0.1 这个时候就发现指数为负了,怎么办?标准规定,存入内存的数字需要加上中间数,float的中间数是127,double的中间数是1023。
E的两种特殊情况
第一:E全0
什么情况下,E会为全零,是不是原来的指数是-127,可以知道,2的-127是很小的一个数,所以这时指数的真实值就是1-127(或者1-1023),而取出来的M前面不补1而是补0,这样做是为了接近正负0
第二:E全1
这时候,有效数字全为0,表示正负无限大
比方说,就以0.5 为例,来看看内存中是如何存储的
#include<stdio.h>
int main()
{
float a = 0.5f; //0.1 二进制表示
//(-1)^0 * 1.0 *2^(-1)
//S =0 M =1.0 E = -1
//提到说,内存中是要加上中间数的127的 会存放126
// 0 01111110 00000000000000000000000
//如何存储(小端存放在内存中)
//是 00 00 00 3f
return 0;
}
3、一道有趣的题
#include<stdio.h>
int main()
{
int n = 5;
//00000000000000000000000000000101
float *pn = (float*)&n;
printf("%d\n",n); //5
printf("%f\n",*pn); //0.000000
*pn = 9.0f;//二进制是 1001.0
printf("%f\n",*pn);//9.000000
//
//1001.0 = (-1)^0 *1.0010*2^3
//M = 3+127 = 130
//0 1000001000 100000000000000000000
printf("%d\n",n);//会按照十进制有符号数打印存放在pn中的值 会打印 1,091,567,616
return 0;
}
pn指向的那个地址所对应的元素,如果以浮点型输出,那么就会把内存中存放的数据当成浮点数存放的规则,第一位符号位,后面八位是指数位,最后23位是表示有效数字。这时发现,指数位全部为0,所以以%f 打印的时候,就会输出0;
同理,看看输出效果
四、总结
本文介绍了数据在内存中的存储,我后续还会接着这个详细介绍数据的取值范围,欢迎交流,欢迎互动,如果你觉得这篇博客还不错,麻烦您点赞关注博主,后续还会带来很多优质的博客。