深度剖析数据在内存中的存储

Posted 雨轩(小宇)

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深度剖析数据在内存中的存储相关的知识,希望对你有一定的参考价值。

1.数据类型详细介绍

这里是CPU为32位的字节数

char        //字符数据类型	1字节
short       //短整型			2字节
int         //整形			4字节
long        //长整型			4字节
long long   //更长的整形		8字节
float       //单精度浮点数	4字节
double      //双精度浮点数	8字节

类型的意义:

  1. 使用这个类型开辟内存空间的大小(大小决定了使用范围)。
  2. 看待内存的视角

视角:比如整形int是4个字节,但在char中只能取到1个字节

	int a = 0x11223344;   
	char b = a;		
	//这里取的是a的最低位0x44 10进制为68 所以b=D
	printf("%c", b);

在这里插入图片描述

2. 整形在内存中的存储:原码、反码、补码

类型分类

整形:
charunsigned charsigned char  // char表示为ascll码,故为整形
shortunsigned short [int]signed short [int]
intunsigned intsigned int
longunsigned long [int]signed long [int]
浮点型:
floatdouble
构造类型:
	结构体类型: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+-100000000000000000000000000000001
	+
	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. 大小端字节序介绍及判断

直接输入1次#,并按下space后,将生成1级标题。输入2次#,并按下space后,将生成2级标题。以此类推,我们支持6级标题。有助于使用语法后生成一个完美的目录。

这里看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)
==9.0 -> 1001.0 ->(-1)^0 1.001 2^3 -> s=0, M=1.001,E=3+127=130==
符号位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

数据在内存中的存储就更新完了!!!欢迎大家前来指点

以上是关于深度剖析数据在内存中的存储的主要内容,如果未能解决你的问题,请参考以下文章

深度剖析数据在内存中的存储

深度剖析数据在内存中的存储1——数据类型

C语言之深度剖析数据在内存中的存储

深度剖析数据在内存中的存储

深度剖析数据在内存中的存储

深度剖析数据在内存中的存储之浮点型在内存中的存储