【C 数据存储详解】(1)——深度剖析整形数据在内存中的存储

简介: 【C 数据存储详解】(1)——深度剖析整形数据在内存中的存储

今天我们一起来学习一下C语言中的整型数据是如何在内存中存储的!!!


一.数据类型介绍

1.类型的意义

我们已经学习过了一些基本的内置类型:


char //字符数据类型

short //短整型

int //整形

long //长整型

long long //更长的整形

float //单精度浮点数

double //双精度浮点数


以及他们所占存储空间的大小(单位是字节):

#include <stdio.h>
int main()
{
    printf("%d\n", sizeof(char));
    printf("%d\n", sizeof(short));
    printf("%d\n", sizeof(int));
    printf("%d\n", sizeof(long));
    printf("%d\n", sizeof(long long));
    printf("%d\n", sizeof(float));
    printf("%d\n", sizeof(double));
    printf("%d\n", sizeof(long double));
    return 0; }

d0933bd8e0734138bfea1050853ed15a.png

C语言规定了这么多不同的数据类型,那么它们有什么意义呢?


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

2. 如何看待内存空间的视角。

2.类型的基本归类

下面我们对C语言中的数据类型做一个基本的归类:

(1).整型家族


1.char

unsigned char

signed char


2.short

unsigned short [int]

signed short [int]


3.int

unsigned int

signed int


4.long

unsigned long [int]

signed long [int]


这里大家可能会有疑惑,char不是字符类型吗?为啥归到整型里面了。


这是因为每个字符都有对应的ASCII码值,这些字符在内存中存储的时候,实际就是存放的ASCII码值,而ASCII码值都是整数,所以将char也归类到整型里面了


在这里还要给大家提醒一点,就是:


对于char来说,C语言本身并没有明确规定我们定义一个char类型的变量,它到unsigned char 还是 signed char 。 这个取决于编译器,不同的编译器情况可能不同,但是在绝大多数编译器上都是signed char 。

但是,对于short,int,long ,C语言规定了

1.short 就是signed short

2.int 就是 signed int

3.long 就是 signed long


(2).浮点数家族


float 单精度浮点型

double 双精度浮点型


(3).构造类型


数组类型

结构体类型 struct

枚举类型 enum

联合类型 union


(4).指针类型


int pi;

char pc;

float pf;

void pv;

0df6da820ea54658a3f0aa523d65778a.png

(5).空类型


void 表示空类型(无类型)

通常应用于函数的返回类型、函数的参数、指针类型


二.详解整型在内存中的存储

我们之前讲过一个变量的创建是要在内存中开辟空间的。空间的大小是根据不同的类型而决定的。


那接下来我们谈谈数据在所开辟内存中到底是如何存储的?

比如:

int a = 20;
int b = -10;

我们知道为 a 分配四个字节的空间。

那如何存储?

来了解下面的概念:


1. 原码、反码、补码

计算机中的整数有三种2进制表示方法,即原码、反码和补码。

三种表示方法均有符号位和数值位两部分:

符号位都是用0表示“正”,用1表示“负”。

而数值位:

正数的原、反、补码都相同。

负整数的三种表示方法各不相同。


下面来介绍一下什么时是原码、反码、补码:

原码:


直接将数值按照正负数的形式翻译成二进制就可以得到原码。


补码:


将原码的符号位不变,其他位依次按位取反就可以得到反码。


反码:


反码+1就得到补码。


举个例子:

f00415cb0880456c80c05937fd6d8377.png

再看一个负数:

c9ba3f76ad544acdb98724c2493dac35.png

整数的2进制表示方法有原码、反码和补码,那内存中存的到底是啥哪?


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


为什么呢?


在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;

同时,加法和减法也可以统一处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是相同的

,不需要额外的硬件电路。


下面来解释一下:


1. 可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理


因为CPU只有加法器,所以对于1-1这样的表达式CPU要处理成1+(-1)来进行计算的。

而如果直接将两个操作数的原码进行相加,是可能会出错的:

举个例子:

1a8753b321574cf68b1df88e43bf7c74.png

1+(-1),我们用原码相加,得到错误的结果

用补码计算:

f2106145e58e4debb3e6c092bf93b022.png

最终用补码相加得到的结果才是正确的!!!


2.补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。


我们通过原码得到补码的方法是:

原码的符号位不变,其它位按位取反得到反码,反码加1,得到补码;

其实补码转到原码也可以用同样的方法。


然后我们创建两个变量,看一下,内存给我们展示出来的是什么样子的:

db0b1f1bd122469da0769102e3bb6ce2.png

我们可以看到对于a和b分别存储的是补码。但是我们发现顺序有点不对劲,好像是相反的。

这是又为什么?


2.大小端介绍

上面我们发现,对于a和b分别存储的是补码。但是我们发现顺序有点不对劲,好像是相反的,为什么?


学完下面的内容,我们就清楚了。

什么大端小端:


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


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


解释一下:

5badc71ca232421c9523aa2dae23800f.png

现在再看这张图我们就明白为什么顺序有点不对劲了。

4926b95dded643e5bfe618365d5d39f2.png

因为在vs2022上,采用的是小端存储模式。


那为什么会有大小端呢?


为什么会有大小端模式之分呢?这是因为在计算机系统中,我们是以字节为单位的,每个地址单元

都对应着一个字节,一个字节为8 bit。但是在C语言中除了8 bit的char之外,还有16 bit的short型,32 bit的long型(要看具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。


例如:一个 16bit 的 short 型 x ,在内存中的地址为 0x0010 , x 的值为 0x1122 ,那么 0x11 为高字节, 0x22 为低字节。对于大端模式,就将 0x11 放在低地址中,即 0x0010 中, 0x22 放在高地址中,即 0x0011 中。小端模式,刚好相反。我们常用的 X86 结构是小端模式,而 KEIL C51 则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。


3.百度2015年系统工程师笔试题讲解

那么我们接下来做一道练习题,这道题是百度2015年系统工程师笔试题:


请简述大端字节序和小端字节序的概念,设计一个小程序来判断当前机器的字节序。


概念我们上面已经说过了,那怎么设计程序呢?我们来思考一下:


我们可以用整数1来帮助判断,取出1的第一个字节的内容,1的补码是:00000000000000000000000000000001,16进制是:00 00 00 01;

如果第一个字节的值是0(高位在低地址),则为大端;

如果第一个字节的值是1(低位在低地址),则为小端。

(注:我们取出的第一个字节是处在低地址的那一个字节)

afefa61b4ef842408d7feda9c454f1a0.png

上代码:

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

我们已经知道了vs上是小端,我们来一下看结果对不对:

72436107aa964b1f86dd1da538bfcca1.png

以上就是对整型在内存中如何存储的详细介绍,欢迎大家指正,我们一起进步!!!

00f04406d8ba4137945e709d108e6f55.png


目录
相关文章
|
20天前
|
监控 算法 应用服务中间件
“四两拨千斤” —— 1.2MB 数据如何吃掉 10GB 内存
一个特殊请求引发服务器内存用量暴涨进而导致进程 OOM 的惨案。
|
20天前
|
存储 C语言
数据在内存中的存储方式
本文介绍了计算机中整数和浮点数的存储方式,包括整数的原码、反码、补码,以及浮点数的IEEE754标准存储格式。同时,探讨了大小端字节序的概念及其判断方法,通过实例代码展示了这些概念的实际应用。
41 1
|
24天前
|
存储
共用体在内存中如何存储数据
共用体(Union)在内存中为所有成员分配同一段内存空间,大小等于最大成员所需的空间。这意味着所有成员共享同一块内存,但同一时间只能存储其中一个成员的数据,无法同时保存多个成员的值。
|
4月前
|
存储 分布式计算 Hadoop
HadoopCPU、内存、存储限制
【7月更文挑战第13天】
276 14
|
3月前
|
存储 编译器 C语言
【C语言篇】数据在内存中的存储(超详细)
浮点数就采⽤下⾯的规则表⽰,即指数E的真实值加上127(或1023),再将有效数字M去掉整数部分的1。
360 0
|
28天前
|
存储 弹性计算 算法
前端大模型应用笔记(四):如何在资源受限例如1核和1G内存的端侧或ECS上运行一个合适的向量存储库及如何优化
本文探讨了在资源受限的嵌入式设备(如1核处理器和1GB内存)上实现高效向量存储和检索的方法,旨在支持端侧大模型应用。文章分析了Annoy、HNSWLib、NMSLib、FLANN、VP-Trees和Lshbox等向量存储库的特点与适用场景,推荐Annoy作为多数情况下的首选方案,并提出了数据预处理、索引优化、查询优化等策略以提升性能。通过这些方法,即使在资源受限的环境中也能实现高效的向量检索。
|
1月前
|
存储 编译器
数据在内存中的存储
数据在内存中的存储
37 4
|
1月前
|
存储 Java
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
这篇文章详细地介绍了Java对象的创建过程、内存布局、对象头的MarkWord、对象的定位方式以及对象的分配策略,并深入探讨了happens-before原则以确保多线程环境下的正确同步。
51 0
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
|
1月前
|
存储 机器学习/深度学习 人工智能
数据在内存中的存储
数据在内存中的存储
|
1月前
|
存储 C语言
深入C语言内存:数据在内存中的存储
深入C语言内存:数据在内存中的存储