数据在内存是怎样存储的?

简介: C语言数据类型在内存中的存储方式

在C语言中,有很多数据类型,同时,我们数据类型所占空间大小不同,但是我们分的类型就仅仅只是所占大小不同吗?int和float同样占了四个字节,那他们为什么表示的东西不同?那今天就带大家一群探究其中的奥秘。

目录

数据类型介绍

基础数据类型

整型

指针类型

构造类型

空类型

整型的存储

原码、反码、补码

负数的原码

负数的反码

负数的补码

那为什么我们的整型在内存中存储的反码呢?

储存模式

大端存储

小端存储

浮点型的存储

浮点型存储规则


数据类型介绍

基础数据类型

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

image.gif

上面这些是我们数据类型的基本分类,细分整型还有无符号和有符号的整型,还有指针、空类型和一些构造类型。

整型

char
    unsigned char            //无符号字符整型
    signed char              //有符号字符整型
short
    unsigned short [int]     //无符号短整型([]内可以省略)
    signed short [int]       //有符号短整型([]内可以省略)
int
    unsigned int             //无符号整型
    signed int               //有符号整型
long
    unsigned long [int]      //无符号长整型([]内可以省略)
     signed long [int]       //有符号长整型([]内可以省略)

image.gif

指针类型

int *p;
char *p;
float* p;
void* p;

image.gif

构造类型

> 数组类型          //int[]、char[]等
> 结构体类型 struct
> 枚举类型 enum
> 联合类型 union

image.gif

空类型

void

image.gif

void通常用于我们的函数返回类型、函数的参数、指针类型等等。

我们分成这么多的类型,其意义是开辟的空间不同和以上面视角看待内存,每种类型在我们内存的存储方式是有所不同的。

整型的存储

我们知道,数据类型的不同决定了开辟空间的大小不同和存储方式的不同,那我们的整型是怎么存储在内存中的?就比如

int a=20;
int b=-10;

image.gif

a和b都是int类型的变量,所占大小都是四个字节,那他们在内存空间是怎么样的?怎么储存在我们的内存里面?

原码、反码、补码

在计算机中,能直接识别的是二进制,而我们的二进制有三种表示形式,就是我们的原码、反码、补码。内存中储存的就是我们的补码,不管是哪一种表示方法,都有符号位和数值位两部分组成,符号位就是开头的第一位,0表示正数,而1表示复数,对于无符号的整型则都是数值位。对于二进制而言,正数的原反补三码是相同的,负数的则都不同。

负数的原码

负数的原码就是我们数本身转换成二进制的码,不需要任何的变换,比如我们上面的a和b的原码就是

int a=20;
//00000000000000000000000000010100  原码
int b=-10;
//10000000000000000000000000001010  原码

image.gif

负数的反码

负数的反码是将我们的二进制位都取反,1变成0,0变成1,但是我们的符号位是不变的。

int b=-10;
//10000000000000000000000000001010  原码
//11111111111111111111111111110101  反码

image.gif

负数的补码

反码+1得到补码。

int b=-10;
//10000000000000000000000000001010  原码
//11111111111111111111111111110101  反码
//11111111111111111111111111110110  补码

image.gif

对于我们的原反补来说,原码变成补码就是全部取反然后+1,补码变成原码同样,可以全部取反然后+1,也可以先-1,然后在进行全部取反。

那为什么我们的整型在内存中存储的反码呢?

对于计算机而言,使用补码可以将我们的符号位和数值位一起计算,同时,对于计算机而言,cpu是只有加法器的,所以在计算途中,如果是原码的话,计算出的结果是不对的。同时我们还可以通过调试中的内存来查看我们数据在内存中是什么样子的,我们的a和b在内存中是

image.gif编辑

image.gif编辑

我们看到他们在内存中并不是一串串的二进制编码,那不是二进制这些是什么呢?这些其实就是我们的补码变成了十六进制的形式放在了我们的内存当中,我们可以将补码转成十六进制看看是不是和图片里面的是否相同

int a=20;
//00000000000000000000000000010100  补码
//   00      00      00      14
int b=-10;
//11111111111111111111111111110110  补码
//   ff      ff      ff      f6

image.gif

我们发现,是十六进制但是缺是倒着放在了我们的内存当中,这是为什么呢?

储存模式

对于计算机而言,我们开内存中开辟的空间都是以字节为单位的,像char的一个字节,int的四个字节,那字节多了,计算机的存放顺序就成为了一个问题,这个时候就出现了两种存储模式,大端和小端

大端存储

大端存储指的是将我们的数据的低位放在高地址上,数据的高位放在低地址上,看上去是按照顺序存放进去的。比如我们的a和b,在大端存储的计算机上,他们的存储就如下面所示

int a=20;
//00000000000000000000000000010100  补码
//   00      00      00      14
//0x00 00 00 14   大端存储
int b=-10;
//11111111111111111111111111110110  补码
//   ff      ff      ff      f6
//0xff ff ff f6   大端存储

image.gif

小端存储

小端存储和大端存储时反着来的,指的是将我们的数据的低位放在低地址上,数据的高位放在高地址上,视觉观感上是倒着放但是没有完全倒着放。图片上所示的就是我们的小端储存。

浮点型的存储

那我们知道了整型在内存中的存储了,那浮点型是怎么存储在内存中的呢?请大家和我来一起看一道有意思的题:

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;
}

image.gif

我们来想一下,程序应该输出什么?都是九吗?还是另有答案?那接下来我们就揭晓答案

image.gif编辑

我们看到,为什么同样的都是n,因为让打印的不同,结果就会各不相同?并且差异很大?我们上面说到了,数据类型的不同,代表的开辟空间大小的不同和观看内存方式的不同,所以这也直接说明了在内存中,浮点型和整型的存储方式有很大的差距。

浮点型存储规则

我们既然知道了浮点型和整型存储是不一样的,那具体不一样在哪里?为什么同样的一组数,因为看待它的方式不同,导致的结果差异很大?这里就需要了解到一个组织IEEE(国际电气和电子工程协会),IEEE有一条标准,IEEE754规定,对于任意的一个二进制浮点数可以表示为以下的形式:

(-1)^S*M*2^E

其中(-1)^S表示的是符号位,S为0表示整数,为1表示负数,M表示有效数字,2^E就是我们的指数位。。举例来说:5.5就可以按照这个表示方法表示为(-1)^S*1.011*2^2,我们是怎么得到这三个数字的呢?S 是表示我们的正负数,我们可以一眼看出是正是负然后得到,那ME呢?我们知道,5.5的变成二进制是101.1,将我们的小数点挪到我们第一位之后,就变成了1.011,这就是我们得到的M的方法,那E其实就是和我们的科学计数法10的几次一样了,那我们知道了这三个数字有什么用呢?IEEE在754标准中还规定了,每个数字应该放在哪,怎么放

单精度

image.gif编辑

双精度

image.gif编辑

S

S相对来说并没有什么规则,和有符号位的整数一样。

M

对于我们的M 来说,因为是二进制,所以M 的范围就是在1~2这个区间,所以在存储的时候一般是将小数点之前的1省略掉,这个时候就节省了一个有效数字位,我们的单精度23位就可以存储24位了,同理双精度也从52位变成了53位,

E

E相较来是它们三个中最复杂的,首先,它是一个无符号的整数,所以对于我们的单精度来说,取值范围是0~255,双精度是0~2047,但是上面说,E的得到和科学计数法中10的几次方是一样的,可以得到负数,所以我们都是得到E之后,如果是单精度就加128,双精度就加1024,就例如我现在的E 是-1,那就需要加上127,得到126,然后把126变成二进制放到E 的位置,对于E还有一些注意事项

E不能全为0或者全为1

当E 全为0时,表示这个数字时个无限接近于0的数字,这个时候我们的精度时不够用的,所以这个时候一般表现为0或者十分接近于0的数字。

当E 全为1 时,这个时候如果我们的有效数字M 为0,则表示我们这个数无限大,表示正负无穷大。

相关文章
|
12天前
|
存储 编译器 数据处理
C 语言结构体与位域:高效数据组织与内存优化
C语言中的结构体与位域是实现高效数据组织和内存优化的重要工具。结构体允许将不同类型的数据组合成一个整体,而位域则进一步允许对结构体成员的位进行精细控制,以节省内存空间。两者结合使用,可在嵌入式系统等资源受限环境中发挥巨大作用。
35 11
|
2月前
|
监控 算法 应用服务中间件
“四两拨千斤” —— 1.2MB 数据如何吃掉 10GB 内存
一个特殊请求引发服务器内存用量暴涨进而导致进程 OOM 的惨案。
|
2月前
|
存储 C语言
数据在内存中的存储方式
本文介绍了计算机中整数和浮点数的存储方式,包括整数的原码、反码、补码,以及浮点数的IEEE754标准存储格式。同时,探讨了大小端字节序的概念及其判断方法,通过实例代码展示了这些概念的实际应用。
64 1
|
2月前
|
存储
共用体在内存中如何存储数据
共用体(Union)在内存中为所有成员分配同一段内存空间,大小等于最大成员所需的空间。这意味着所有成员共享同一块内存,但同一时间只能存储其中一个成员的数据,无法同时保存多个成员的值。
|
2月前
|
监控 Java easyexcel
面试官:POI大量数据读取内存溢出?如何解决?
【10月更文挑战第14天】 在处理大量数据时,使用Apache POI库读取Excel文件可能会导致内存溢出的问题。这是因为POI在读取Excel文件时,会将整个文档加载到内存中,如果文件过大,就会消耗大量内存。以下是一些解决这一问题的策略:
130 1
|
2月前
|
存储 弹性计算 算法
前端大模型应用笔记(四):如何在资源受限例如1核和1G内存的端侧或ECS上运行一个合适的向量存储库及如何优化
本文探讨了在资源受限的嵌入式设备(如1核处理器和1GB内存)上实现高效向量存储和检索的方法,旨在支持端侧大模型应用。文章分析了Annoy、HNSWLib、NMSLib、FLANN、VP-Trees和Lshbox等向量存储库的特点与适用场景,推荐Annoy作为多数情况下的首选方案,并提出了数据预处理、索引优化、查询优化等策略以提升性能。通过这些方法,即使在资源受限的环境中也能实现高效的向量检索。
|
2月前
|
缓存 安全 Java
使用 Java 内存模型解决多线程中的数据竞争问题
【10月更文挑战第11天】在 Java 多线程编程中,数据竞争是一个常见问题。通过使用 `synchronized` 关键字、`volatile` 关键字、原子类、显式锁、避免共享可变数据、合理设计数据结构、遵循线程安全原则和使用线程池等方法,可以有效解决数据竞争问题,确保程序的正确性和稳定性。
44 2
|
4月前
|
存储 编译器 C语言
【C语言篇】数据在内存中的存储(超详细)
浮点数就采⽤下⾯的规则表⽰,即指数E的真实值加上127(或1023),再将有效数字M去掉整数部分的1。
394 0
|
2月前
|
存储 编译器
数据在内存中的存储
数据在内存中的存储
42 4
|
2月前
|
存储 Java
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
这篇文章详细地介绍了Java对象的创建过程、内存布局、对象头的MarkWord、对象的定位方式以及对象的分配策略,并深入探讨了happens-before原则以确保多线程环境下的正确同步。
57 0
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配