深度剖析数据在内存中的存储
Posted 雨轩(小宇)
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深度剖析数据在内存中的存储相关的知识,希望对你有一定的参考价值。
1.数据类型详细介绍
这里是CPU为32位的字节数
char //字符数据类型 1字节
short //短整型 2字节
int //整形 4字节
long //长整型 4字节
long long //更长的整形 8字节
float //单精度浮点数 4字节
double //双精度浮点数 8字节
类型的意义:
- 使用这个类型开辟内存空间的大小(大小决定了使用范围)。
- 看待内存的视角
视角:比如整形int是4个字节,但在char中只能取到1个字节
int a = 0x11223344;
char b = a;
//这里取的是a的最低位0x44 10进制为68 所以b=D
printf("%c", b);
2. 整形在内存中的存储:原码、反码、补码
类型分类
整形:
char: unsigned char、signed char // char表示为ascll码,故为整形
short:unsigned short [int]、signed short [int]
int: unsigned int、signed int
long: unsigned long [int]、signed long [int]
浮点型:
float、double
构造类型:
结构体类型:struct
枚举类型:enum
联合类型:union
指针类型:int *p,char *p,float *p,void *p
空类型:
void 表示空类型(无类型)
通常应用于函数的返回类型、函数的参数、指针类型。
空间的大小是根据不同的类型而决定的。
- 对于整形来说:数据存放内存中其实存放的是补码。
int a=10; //这里为a开辟四个字节的空间,以补码的形式进行存储。
原码、反码、补码
- 正数的原码、反码、补码一样
- 负数的反码=符号位不变,其余位取反,补码=反码+1
int a1=10; //a1的原码=反码=补码=00000000000000000000000000001010;
int a2=-10;
//a2的原码=10000000000000000000000000001010;
//a2的反码=11111111111111111111111111110101;
//a2的补码=11111111111111111111111111110110;
来看看下面的例题
1.
//输出什么?
#include <stdio.h>
int main()
{
// 范围-128-127
char a= -1; // 原码:10000001 反码:11111110 补码:11111111
// 发生截断只能存在8个bit位
// 范围-128-127
signed char b=-1; // 有符号输出,所以就是原码-1
// 范围0-255
unsigned char c=-1; // 无符号类型,%d输出整形提升
//整形提升000000000000000000000011111111
printf("a=%d,b=%d,c=%d",a,b,c);
-1 -1 255
return 0;
}
2.
#include <stdio.h>
int main()
{
char a = -128;
printf("%u\\n",a); //%u按照无符号形式输出,为4294967168
// 原码:10000000 反码:11111111 补码:100000000
// 以%u输出,补码最高位就不是符号位了,此时补码就是原码了
// 以11111111111111111111111100000000输出,所以为4294967168
return 0;
}
3.
#include <stdio.h>
int main()
{
// 同上一道题
char a = 128;
printf("%u\\n",a);
return 0; }
数据用补码存储的原因
- 可以将符号位与数据域统一处理
- CPU只有加法器,加法和减法也可以统一处理
1-1;
CPU只有加法器,表示为:1+(-1)
00000000000000000000000000000001
+
10000000000000000000000000000001
=
10000000000000000000000000000010
这里如果用原码算的法就是-2
所以只能用补码去算
00000000000000000000000000000001
+
11111111111111111111111111111111
=
100000000000000000000000000000000
由于只能存储32位,最高位进1,变为0,所以结果为0
4.
int i= -20; //补码:111111111111111111111111111101100
unsigned int j = 10; //补码000000000000000000000000000001010
printf("%d\\n", i+j); // -10
//加起来:100000000000000000000000000001010
//按照补码的形式进行运算,最后格式化成为有符号整数
5.
unsigned int i;
// i为无符号整形,变为-1,=1111111111111111111111111111111
// 死循环
for(i = 9; i >= 0; i--)
{
printf("%u\\n",i);
}
char类型的存储
有符号的char范围是:-128-127
-128:10000000000000000000000010000000
11111111111111111111111101111111
11111111111111111111111110000000
6.
int main()
{
char a[1000];
int i;
for(i=0; i<1000; i++)
{
a[i] = -1-i;
}
printf("%d",strlen(a)); //255, -128-127这255
return 0; }
7.
#include <stdio.h>
unsigned char i = 0;
int main()
{
for(i = 0;i<=255;i++)
{
printf("hello world\\n");
//死循环,跟第五题一样,无符号整形的范围,看char的图解!!!
}
return 0; }
3. 大小端字节序介绍及判断
这里看a和b的存储顺序是不是有点不同,补码反序存储的
- 大端(存储)模式:是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中。
- 小端(存储)模式:是指数据的低位保存在内存的低地址中,而数据的高位,,保存在内存的高地址中。
简单记忆
1、大端存储:高位低地址,低位高地址
2、小端存储:高位高地址,低位低地址
产生大端存储和小端存储的原因
- 计算机系统中,是以字节为单位的,每个地址单元都对应着一
个字节,一个字节为8bit。 - 在C语言中,有上面所说的几种数据类型,char、int等等,例外对于大多数编译器来说,是32位的编译器,由于寄存器宽度大于一个字节,所以必然存在将字节顺序安排的问题,大端存储和小端存储随之产生。
百度2015年系统工程师笔试题:
请简述大端字节序和小端字节序的概念,设计一个小程序来判断当前机器的字节序。(10分)
4.浮点型在内存中的存储
常见的浮点数:
3.14159 1E10
- 浮点数家族包括: float、double、long double 类型。
- 浮点数表示的范围:float.h中定义
先来看一个浮点数存储的例子
整数和浮点型在内存中的存储是不一样的!!!
int main()
{
int n = 9;
float *pFloat = (float *)&n;
printf("n的值为:%d\\n",n);
printf("*pFloat的值为:%f\\n",*pFloat); //浮点型的视角
*pFloat = 9.0;
printf("num的值为:%d\\n",n);
printf("*pFloat的值为:%f\\n",*pFloat);
return 0; }
大家觉得答案是什么呢?
答案:9、0.000000、1091567616、9.000000
如果做错了,先不要着急,先看看下面的内容!!!
根据国际标准IEEE(电气和电子工程协会) 754,任意一个二进制浮点数V可以表示成下面的形式:
- (-1)^S * M * 2^E
- (-1)^S表示符号位。
- 当S=0,V为正数;当S=1,V为负数。
- M表示有效数字,大于等于1,小于2。
- 2^E表示指数位。
例如十进制的5.0,二进制是101.0,相当于1.01*2^2
按照IEEE标准,此时S=0、M=1.01、E=2
对于十进制-5.0,此时S=1、M=1.01、E=2
IEEE 754规定: 对于32位的浮点数,最高的1位是符号位s,接着的8位是指数E,剩下的23位为有效数字M。
对于64位的浮点数,最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M。
IEEE 754对有效数字M和指数E,还有一些特别规定。
- 前面提到,1≤M<2,其中M=1.xxxxxx,其中xxxxxx是小数部分
- 计算机内部保存M时,第一位1总是被舍去,只保留xxxxxx小数部分
- 比如保存1.01的时候,只保存01,等读取时,在加第一位的1加上(节省1位有效数字)
- 以32位浮点型为例,M只有23位,第一位1舍去之后,就可以保留24位有效数字
对于指数E
- E为一个无符号整数(unsigned int)
- 如果E为8位,它的取值范围为0~255;如果E为11位,它的 取值范围为0~2047。
- 存入内存时E的真实值必须再加上一个中间数(因为E可能为负数)
- 对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。
指数E从内存中取出还可以再分成三种情况:
- E不全为0或不全为1:
指数E的计算值减去127(或1023),得到真实值,再将有效数字M前 加上第一位的1。 比如: 0.5(1/2)的二进制形式为0.1,由于规定正数部分必须为1,即将小数点右移1位,则为1.0*2^(-1),其阶码为-1+127=126,表示为01111110,而尾数1.0去掉整数部分为0,补齐0到23,00000000000000000000000,则其二进制表示形式为:
- E全为0:
浮点数的指数E等于1-127(或者1-1023)即为真实值, 有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。(表示±0,以及接近于0的很小的数字。) - E全为1:
如果有效数字M全为0,表示±无穷大(正负取决于符号位s);
现在进行刚刚例题的讲解
int main()
{
int n = 9;
float *pFloat = (float *)&n;
printf("n的值为:%d\\n",n); // 整形的角度
printf("*pFloat的值为:%f\\n",*pFloat); //浮点型的视角
*pFloat = 9.0;
printf("num的值为:%d\\n",n);
printf("*pFloat的值为:%f\\n",*pFloat);
return 0; }
1、9的二进制是0000 0000 0000 0000 0000 0000 0000 1001,整形正值原码即补码,所以输出为9。
printf("n的值为:%d\\n",n); // 9
2、pFloat将n转换为浮点型指针(float*),此时角度为float型
此时在浮点型中:0 00000000000 00000000000000001001
符号位S=0
指数E=00000000
23位有效数字M=00000000000000001001
按照IEEE规定浮点数就为:
(-1)^0 x0.00000000000000000001001×2(-126)=1.001×2(-146)
这个数很小,接近于0,所以十进制表示小数位0.000000
printf("*pFloat的值为:%f\\n",*pFloat); // 0.000000
3、*pFloat=9.0,浮点型表示为1.001x(2^3)
符号位s=0,有效数字M等于001后面再加20个0,凑满23位,指数E等于3+127=130
此时二进制表示:0 10000010 001 0000 0000 0000 0000 0000
为正,十进制表示为:1091567616
printf("num的值为:%d\\n",n); // 1091567616
4、*pFloat输出就没什么可说的了,输出如下
printf("*pFloat的值为:%f\\n",*pFloat); // 9.0
数据在内存中的存储就更新完了!!!欢迎大家前来指点
以上是关于深度剖析数据在内存中的存储的主要内容,如果未能解决你的问题,请参考以下文章