首页 > 系统相关 >深度剖析数据在内存中的存储

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

时间:2023-05-31 22:01:37浏览次数:40  
标签:10 存储 字节 int 00 剖析 内存

一、 数据的类型

1、C语言中的数据类型

可以分为以下几种:

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

2、在C语言中

char类型占1个字节

short类型占2个字节

int类型占4个字节

long类型没有固定具体的大小,所占空间>=int类型所占空间

long long类型占8个字节

float类型占4个字节

double类型占8个字节……


3、类型的意义

      (1)这个(种)类型开辟空间的大小,开辟空间的大小决定了定义的数据的存储的范围及使用的范围。

      (2)看待开辟内存空间的视角不同。

4、数据的类型

可以划分为五种,分别为:整形类型、浮点类型、指针类型、构造类型、空类型。

整形类型:

深度剖析数据在内存中的存储_字节序_02

      char类型虽然是字符类型,但在存储char定义的变量时,存储的是变量的ASCII码值,ASCII码值是整数,所以char类型属于整形变量的范畴。

      char类型没有具体的规定它是signed char 或 unsigned char,它的类型是什么,是取决于编译器的,不同的编译器对char的类型有着不同的规定。

浮点类型:

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

构造类型:

深度剖析数据在内存中的存储_浮点数_04

大家可能对数组也是构造类型感觉到奇怪,我们定义一个数组,int arr[10] = {0}; 其中,arr是数组的变量名,而int [10]是这个数组的类型,如果将[]内的值从10修改为5,那这个数组的类型就变成了int [5],数组类型发生了改变,所以说数组也是一种构造类型。

指针类型:

深度剖析数据在内存中的存储_字节序_05

空类型:

深度剖析数据在内存中的存储_字节序_06


二、 整数在内存中的存储

任何一个变量存储在内存中都需要开辟一块空间来存放,又因为计算机只认识机器语言,机器语言就是二进制的0和1组成的语言,所以想让计算机识别存放在内的数据,数据在内存中也只能是二进制的。

计算机中有符号数的表示有三种方法:原码、反码、补码。

深度剖析数据在内存中的存储_浮点数_07

注:在有符号数的二进制中,第一个二进制位为符号位,其中0代表整数,1代表负数。但对于无符号数来讲,第一位也是有效的数据位。

举个例子:int a = 10;

因为变量a中存放的是正整数,所以它的原反补码相同,为:


0 0000000000000000000000000001010      —>10的原反补码相同

因为10是个整数,而整形的类型是int,int在内存中占4个字节,又因为1个字节(byte)等于8个比特位(bit),所以10在内存中存储的是32个比特位的二进制数。

再举个例子:int b = -20;

1 0000000000000000000000000010100             —>     -20的原码
1 1111111111111111111111111101011             —>     -20的反码
1 1111111111111111111111111101100             —>     -20的补码

      对于整形来说:数据存放在内存中其实存放的是补码。

三、 大端字节序和小端字节序

1、举个例子

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

深度剖析数据在内存中的存储_浮点数_09

由上图可知,10的在内存中存储的二进制为:

0 0000000000000000000000000001010

-20在内存中存储的二进制为:

1 1111111111111111111111111101100

将二进制转化为16进制之后,应该是00 00 00 0a和ff ff ff ec,可是为什么内存中存放的却是反过来的呢?

这是因为,这里面存在大端存储和小端存储的概念。

2、何为大小端存储

      大端(存储)模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中。

小端(存储)模式,是指数据的低位保存在内存的低地址中,而数据的高位,,保存在内存的高地址中。

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

在内存中存储的是大端字节序或是小端字节序看的是编译器,编译器支持的是大端,内存中存储的就是大端字节序,支持的是小端,内存中存储的就是小端字节序。

可以理解为一个数字是10000000,把这个数字放在地址中时,大端字节序是不管三七二十一,我就从头放到尾。就变成了:

低地址   10  00  00  00   高地址(因为这个数字中,1是权重最大的,最右边的0是权重最小的。)

而小端字节序是你们给我好好的站好,“小个子的”去前面站着,“大个子的”去后面站着。就变成了:

低地址   00  00  00  10   高地址

1个16进制数等于4个2进制数。

深度剖析数据在内存中的存储_浮点数_11

3、如何判断当前机器的字节序

用编程实现:

方法一: 所以当a=1时,如果是小端字节序,内存中存放的应该是01 00 00 00,而大端字节序中存放的是00 00 00 01, 当*p取出第一个字节的时候,小端取出的是01,而大端取出的是00。所以当是小端时,p=1,大端时,p=0。

#include <stdio.h>
int main()
{
	int a = 1;
	char p = (char)&a;
	if (*p == 1)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}
	return 0;
}

方法二: 所以当a=1时,如果是小端字节序,内存中存放的应该是01 00 00 00,而大端字节序中存放的是00 00 00 01, 当p取出第一个字节的时候,小端取出的是01,而大端取出的是00。所以当是小端时,ret=1,大端时,ret=0。

#include <stdio.h>
int check_sys()
{
  int a = 1;
  char p = (char*)&a;
  if (*p == 1)
  {
  	return 1;
  }
  else
  {
  	return 0;
  }
}
int main()
{
  int ret = check_sys();
  if (ret == 1)
  {
  	printf("小端\n");
  }
  else
  {
  	printf("大端\n");
  }
  return 0;
}

方法三:对方法二的优化。

#include <stdio.h>
int check_sys()
{
	int a = 1;
	char* p = (char*)&a;
	return p;//因为p的值为00或01,所以可以直接返回0或1。
}
int main()
{
	int ret = check_sys();
 	if (ret == 1)
 	{
 		printf("小端\n");
	}
	else
 	{
		printf("大端\n");
	}
	return 0;
}

四、 浮点数在内存中的存储

如:3.14、1E10,这样的数字都是浮点数。其中1E10,代表的是1.0*10^10。

1、浮点数的存储规则

国际组织IEE754规定,任何一个二进制浮点数都可以表示成如下情况:

深度剖析数据在内存中的存储_浮点数_12

举个例子:十进制中的13.0,二进制形式是:1101.0,写成科学计数法是:1.1010*2^3。

按照上面的规定可以得出:S=0,M=1.1010,E=3。

另一个数字是-13呢,道理一样的嘛!

二进制形式是:-1101.0 ,写成科学计数法是:-1.1010*2^3,

得出S=1,M=1.1010,E=3。

浮点数在内存中存储的方式为:

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

深度剖析数据在内存中的存储_字节序_14

2、浮点数中E和M的存储规则

IEE754组织对于E和M做了特殊的规定:

M表示的数字范围:1<= M<=2,以单精度浮点数举例,留给M的位置只有23个比特位,如果把前面的1也加入,留给小数的位数就只剩22位了,所以IEE754规定,M的23个比特位都留给小数,当要取出这个浮点数时,再把前面的1加上去。

对于E的存入,因为指数E是一个无符号数,但是指数E可以是负数的,如:0.001,转变为科学计数法就是1.0*10^-3。所以,IEE754规定,对于存入内存时E的真实值必须再加上一个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。

比如13转变成科学计数法后的S为0,E值为3,M为10100000000000000000000, 但是存入内存中的E要加上127,所以存入内存中的E值为3+127=130,转变为二进制为:10000010。完整显示为:0 10000010 10100000000000000000000



3、浮点数中E的取出规则

对于E的取出,IEE754划分了三种情况:

(1) 当E中既有0又有1时

因为存入时加上了个中间数后存入的,当取出的时候要减去这个中间数得到它的真实值后,再把M前面的1补上。还是以上面的13举例:存入内存中的二进制为:

0 1000001010100000000000000000000

先把E加上的127减去,得到00000011,转变为10进制为3,再把M前面的1加上得到1.1010(此处省略19个0),以科学计数法表示得到:1.1010*2^3 —> 1101.0,转变为10进制为13.0。

(2) 当E中全为0时

IEE754规定当E为全0时,E的值直接被认定为:1-127(单精度浮点型) 或 1-1023(双精度浮点型),M前面的1也不加了,直接化为0.xxxxxx。一个数字本来就是0.xxxxx,再乘上一个2的-126次幂,结果是无限接近于0的数字。所以这样是为了表示±0的数字。

(3) 当E中全为1时

IEE754规定,当E为全1时,就算把加上的中间值减去,单精度浮点型还有128呢,1.xxxxxx的数字乘上2的128次幂是一个特别大的数字,此时代表的时±无穷大。

4、浮点数存储与取出

下面的程序输出的结果是什么?

#include <stdio.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;
}

输出结果:

深度剖析数据在内存中的存储_字节序_15

为什么会输出下面的这些数字呢?

解析:定义了一个int型的变量n,n值为9。因为n是整形,指针类型也是int*的,如果想放在*pFloat中,需要将其强制类型转化为*float类型。

第一个printf,以整形存储,再以整形输出所以输出9。

第二个printf,以整形存储,9的二进制为:

0000 0000 0000 0000 0000 0000 0000 1001,

按照浮点数的排列为:

0 0000000000000000000000000001001。

可以看到E全为0,所以就得0。

第三个printf,将*pFloat的值修改为9.0,以浮点数存储再以整形进行输出。9.0转化为2进制为:1001.0,其中S=0,E=3,M=1.001

浮点数二进制的完整显示为:0 1000001000100000000000000000000

因为以%d整形输出,所以就将0 10000011 00100000000000000000000,视为一个有符号的整数进行打印输出,

第四个printf,以浮点型存储以浮点型输出,存储的是9.0,输出的自然就是9.0。9.0在内存中存储的二进制为:

0 10000010 00100000000000000000000

先把E加上的127减去,得到00000011,转变为10进制为3,再把M前面的1加上得到1.0010(此处省略20个0),以科学计数法表示得到:1.0010*2^3 —> 1001.0,转变为10进制为9.0。

结尾:感谢你能看到这里,如果有写的不对的地方请给位不吝赐教,谢谢啦!

深度剖析数据在内存中的存储_浮点数_16

标签:10,存储,字节,int,00,剖析,内存
From: https://blog.51cto.com/u_15865089/6390304

相关文章

  • c#使用内存映射像处理内存一样去快速处理文件
    在.NETCore中,`System.IO.MemoryMappedFiles.MemoryMappedFile`类提供了对内存映射文件的支持。通过将文件映射到内存,你可以在应用程序中直接访问文件的内容,而不需要显式地进行文件的读取和写入操作。内存映射文件允许你将文件的特定区域映射到内存中的一个或多个`MemoryMap......
  • linux 2种方式修改tmp目录的内存大小
    起因,tmp是临时目录,重启系统后目录的文件会清空,但是有时候你安装的软件依赖tmp进行临时存放文件,但tmp目录又太小。使用df-h查看/tmp目录的挂载点是tmpfs,这说明没有物理挂载设备。tmpfs有官方的介绍文章可以在评论区补充,谢谢。方法1:修改/etc/fstab文件的内容。vim/e......
  • 生态共建丨崖山数据库系统与杉岩分布式存储系统完成兼容互认证 
    近日,深圳计算科学研究院(以下简称:深算院)自主研发设计的数据库管理系统YashanDBV22.2产品与深圳市杉岩数据技术有限公司(以下简称:杉岩数据)的分布式存储系统完成兼容性互认证。测试结果表明,双方产品完全兼容,在功能、性能及兼容性方面表现良好,整体运行稳定高效。崖山数据库系统YashanDB......
  • 基于multiprocessing map实现python并行化(全局变量共享 map机制实用向分析 常见问题 p
    转载:(15条消息)基于multiprocessingmap实现python并行化(全局变量共享map机制实用向分析常见问题pandas存储数据)_goto_past的博客-CSDN博客基于multiprocessingmap实现python并行化之前从来没考虑python可以并行化,最近有一个项目需要计算100*100次的遗传算法适应度,每次计算......
  • MS SQL Server 中的存储过程是一种预编译的代码块,可以接收输入参数并返回输出结果,用于
    MSSQLServer中的存储过程是一种预编译的代码块,可以接收输入参数并返回输出结果,用于完成特定的数据库操作。它们是SQLServer中存储逻辑业务的一种常见方式。下面是存储过程的优势和劣势:优势:更高的性能:存储过程在首次执行时会被编译和优化,然后将编译后的执行计划缓存起来,......
  • 多线程-线程池与java内存模型
    多线程-线程池与java内存模型线程池的使用(思路:什么是线程池->他的基本构造以及参数含义->如何使用,使用过程中需要注意什么->有哪些好用的工具类)线程池的基笨概念:首先看一下的继承关系,其次看他的状态,它是利用int的高三位表示状态,比如111表示能接受任务,具体看下面第二章图......
  • thread_local 存储类
     使用thread_local说明符声明的变量仅可在它在其上创建的线程上访问。变量在创建线程时创建,并在销毁线程时销毁。每个线程都有其自己的变量副本。thread_local说明符可以与static或extern合并。可以将thread_local仅应用于数据声明和定义,thread_local不能用于函数......
  • extern 存储类
     extern 存储类用于提供一个全局变量的引用,全局变量对所有的程序文件都是可见的。当您使用'extern'时,对于无法初始化的变量,会把变量名指向一个之前定义过的存储位置。当您有多个文件且定义了一个可以在其他文件中使用的全局变量或函数时,可以在其他文件中使用 extern 来得......
  • 【C++】c++单继承、多继承、菱形继承内存布局(虚函数表结构)
    单继承:只有一个基类和一个派生类classBase{public:virtualvoidfun1(){cout<<"Base::func1()"<<endl;}virtualvoidfun2(){cout<<"Base::func2()"<<endl;}private:intb;......
  • lucene LZ4 会将doc存储在一个chunk里进行Lz4压缩 ES的_source便如此
    默认情况下,Elasticsearch用JSON字符串来表示文档主体保存在 _source 字段中。像其他保存的字段一样,_source 字段也会在写入硬盘前压缩。The_sourceisstoredasabinaryblob(whichiscompressedbyLucenewithdeflateorLZ4)其实就是多个_source合并到一个chunk里......