C语言进阶——数据在内存中的存储

简介: 经过前面博客的介绍,我们的C语言初阶已经学完了。现在我们可以进入更深层次的C语言世界了,而本文是我们进阶的首篇文章,主要是介绍各种数据在内存中的存储情况,比如有符号char的最大值是多少、整型数据与浮点型数据在内存的存储方式有何不同等,学会这些知识能增加我们的内功,真正做到了然于心。🚀🚀🚀

🧮前言


 经过前面博客的介绍,我们的C语言初阶已经学完了。现在我们可以进入更深层次的C语言世界了,而本文是我们进阶的首篇文章,主要是介绍各种数据在内存中的存储情况,比如有符号char的最大值是多少、整型数据与浮点型数据在内存的存储方式有何不同等,学会这些知识能增加我们的内功,真正做到了然于心。🚀🚀🚀


a9409c318ff84fd9fd328cdfdce410c.png


🧮正文


我们C语言中的有七种基本数据类型,可以分为三种:整型、实型、字符型。

C语言中的基本数据类型

short

短整型

%hd 

2字节

int

整型

    %d  

4字节

long

长整型

 %ld 

4-8字节

long long 

更长整型

    %lld  

8字节

float

单精度浮点型

      %f  

4字节

double

双精度浮点型

 %lf   

8字节

char

字符型

 %c   

1字节

💻数据分类


根据各数据的特点,可将数据分为以下几类:

🖥️整型家族


char

当我们选择char时,是否带有符号是由编译器决定的,有符号和无符号 char 的取值范围不同

char 大小为1字节=8比特,因此在 char 中至多有八个可用位。

57908cce8deeb6b6be3fd93e9a3ecc2.pngsigned char 有符号 char

5496f6609f1b370c49ff0c976805e1a.png

unsigned char 无符号 char


a4e382543c9cb7cfc8e1ff5266d5bd5.png


short

short比 char 大1字节,因此所表示范围值会比 char 大很多,最大值同样是无符号表示

d0a876de05e14183073339c624fa9db.png

signed short


67d738e702f5a54237a95c4799d307a.png


unsigned short

28651f6ebbf23ab8f74d02514723b17.png



int


int为标准的4字节,32比特位,有无符号的 int 最大值差别依旧很大


5decb0aafec1ebd88b1f3a6cc52aad3.png


signed int


d0f20e09a59a2adabcbc4ae93b85381.png


unsigned int

ecee770f5f8f75f6a740740764e2340.png



long


long的大小是4~8字节,这里我们取8字节,相当于long long,作为最大的数据类型,long在使用时几乎很难造成溢出,因为比较无符号long最大值就为922京(9*10^18)


01f2f0ec68e90ea6213cda556025ebd.png


signed long

c3dc5eb7ee91dc1658d527cf19f447d.png



unsigned long

5f3ffb86d5216b0c68ec4a5eb2ce2dc.png



🖥️浮点型家族


浮点型家族就两个:float与double,float是4字节,double则是8字节,可表示的范围也是非常大,由于浮点型在内存中存储时比较复杂,不再依靠原反补这套系统,而是拥有属于自己的存储方式。有关浮点型数据在内存中的存储情况,将会专门在后面解释。


🖥️构造家族


构造家族外部依赖性强,有以下四种:

数组类型

  arr[ ]

结构体类型

struct

枚举类型

enum

联合类型

union

构造家族成员都需要依靠外部定义的数据,比如数组,需要定义大小;结构体,需要声明内部的变量成员;枚举类型则需要根据变量数来确定枚举值等


🖥️指针家族


指针家族中包括了各种类型的指针变量,比如常用的有


int*

pi

整型指针,指向整型数据

float*

pf

浮点型指针,指向浮点型数据

char*

pc

  字符型指针,指向字符型数据

void*

pv

空指针,能指向所有的数据,但无法进行操作,作临时指针




🖥️空类型家族


空类型(void)指没有具体的数据类型,通常用于函数返回值、函数参数、临时指针中。

cbd25df226cf64a8262aefa44b020c6.png



💻整型数据在内存中的存储


整型数据有三种状态:原码、反码、补码,原码就是将原数据转换为二进制后的序列,序列中的最高位为符号位(0为正数,1为负数),反码则是将原码除符号位外全部取反(0变为1,1变为0),补码则是在反码的基础上+1,整型数据在内存中都是以补码的形式存储的,因为正数原码、反码、补码均相同,因此我们只有遇到负数时才刻意去求补码。


🖥️原码、反码、补码



原码


将数值向二进制进行转换,要注意符号位,0为正数,1为负数

反码

将原码除符号位外全部取反,比如10000001,取反为11111110

补码

再反码的基础上进行+1,比如11111110,+1后变成11111111

至于为什么需要补码这个概念?


因为CPU中只有加法器,在执行减法操作时会将被减数转换为一个负数,然后再进行相加

31fba06684b8cbca4bf3ca10d916e67.png



补码的产生使得加法转换为减法后的计算结果依旧正确,而且因为转码的运算过程是相同的,不需要借助额外的硬件电路,因此计算速度上几乎无差异。


🖥️大小端字节序


在我们的内存中存在两种不同的存放标准,一种是大端存储,另一种则是小端存储,不同编译器所支持的存储顺序有所不同,比如我们的VS2019,使用的就是小端字节序存储数据。


大端存储

指将数据高位次存放在内存的低地址中,而低位次则是存放在内存的高地址中  

当为大端存储时,十六进制会正着显示

小端存储

指将数据高位次存放在内存的高地址中,而低位次则是存放在内存的低地址中

当为小端存储时,十六进制会倒着显示

4d8f81bff5dc767d791cd0c60510af6.png


79e17dea695484d54cba6209905234a.png

大小端相关笔试题(百度 2015 笔试题)


9a0fd8d6994d3d3970edbf82057dbdf.png

4c398e61482c9b33303bd19a56581d2.png

//判断大小端
int main()
{
  int a = 1;//十六进制表示为 00 00 00 01
  char* pa = (char*)&a;//利用字符型指针访问一字节
  if (*pa == 1)
    printf("小端存储\n");
  else
    printf("大端存储\n");
}

💻浮点型数据在内存中的存储


🖥️存入


浮点数在内存中表示时比较复杂,于是电气和电子工程协会(IEEE)754标准便这样规定了浮点数在内存中存储规则:任何一个浮点数V都可以写成  V=(-1)^S*M*2^E ,其中S控制符号位,为1时V为负数,为0时V为正数;M为有效数字,在1~2这个区间内;2^E则表示指数位。

c18a54f81d6c5d1eabbf2229f25933d.png

e223b1ac6a764c59a59d073ce6f483d.png




由此可见浮点数在内存中的储存与整型完全不一样,也就是说如果在输入(输出)时格式匹配错误,那么数据肯定就是有问题的!!!


单精度浮点型(float)有32比特位,规则在上面,而双精度浮点型(double)有64位,规则跟32位几乎一致,不过在空间分配和指数E的中间值上略有差异

ed9c81fe243662d21513553b66eea79.png




🖥️取出


存入很复杂,取出也很复杂,光是取出的情况就有三种:


1.指数E非全0或非全1时,常规取出,如果存的时候加了中间值127(或1023)取的时候就要减去中间值127(或1023),这是比较常见的取出形式。

2.指数E为全0时,若指数E为全0,说明初始E为-127,可以看出原浮点数是一个非常小的数,无限接近于0,因此这个数字取出来将会是一个很小很小的数。

3.指数E为全1时,若有效数字M全为0,表示正负无穷大。

🖥️例题


模拟将整型存入浮点型,将浮点型存入整型的场景


9e84fa7c11800b420584577f06ab2c8.png

//模拟
int main()
{
  int a = 9;
  float* pa = &a;
  printf("%d\n", a);
  printf("%f\n", *pa);
  *pa = 9.0;
  printf("%d\n", a);
  printf("%f\n", *pa);
  return 0;
}

🧮总结


 数据类型算是C语言中的底层部分了,不同数据类型的数值轮回也是合情合理,在做题时注意看是否有符号,这样在进行计算时就可以判断出是否接近边界;不同类型的数据都有其应用场景,只有做到场景匹配了,程序效率才会最大化。总的来说,无论是反码相加还是浮点数的存储,都是非常巧妙的设计,是无数前辈绞尽脑汁的最优解,正是因为有了这些规则,今天我们才能看到如此完善的C语言体系。今天,我们站在巨人的肩膀上,明天,我们也许就能实现巨人们远大的理想!🌌


 如果你觉得本文写的还不错的话,期待留下一个小小的赞👍,你的支持是我分享的最大动力!


 如果本文有不足或错误的地方,随时欢迎指出,我会在第一时间改正


目录
相关文章
|
5天前
|
存储 C语言
C语言如何使用结构体和指针来操作动态分配的内存
在C语言中,通过定义结构体并使用指向该结构体的指针,可以对动态分配的内存进行操作。首先利用 `malloc` 或 `calloc` 分配内存,然后通过指针访问和修改结构体成员,最后用 `free` 释放内存,实现资源的有效管理。
30 12
|
5天前
|
存储 编译器 数据处理
C 语言结构体与位域:高效数据组织与内存优化
C语言中的结构体与位域是实现高效数据组织和内存优化的重要工具。结构体允许将不同类型的数据组合成一个整体,而位域则进一步允许对结构体成员的位进行精细控制,以节省内存空间。两者结合使用,可在嵌入式系统等资源受限环境中发挥巨大作用。
24 11
|
2天前
|
存储 C语言 计算机视觉
在C语言中指针数组和数组指针在动态内存分配中的应用
在C语言中,指针数组和数组指针均可用于动态内存分配。指针数组是数组的每个元素都是指针,可用于指向多个动态分配的内存块;数组指针则指向一个数组,可动态分配和管理大型数据结构。两者结合使用,灵活高效地管理内存。
|
28天前
|
监控 算法 应用服务中间件
“四两拨千斤” —— 1.2MB 数据如何吃掉 10GB 内存
一个特殊请求引发服务器内存用量暴涨进而导致进程 OOM 的惨案。
|
26天前
|
C语言
【c语言】动态内存管理
本文介绍了C语言中的动态内存管理,包括其必要性及相关的四个函数:`malloc`、``calloc``、`realloc`和`free`。`malloc`用于申请内存,`calloc`申请并初始化内存,`realloc`调整内存大小,`free`释放内存。文章还列举了常见的动态内存管理错误,如空指针解引用、越界访问、错误释放等,并提供了示例代码帮助理解。
36 3
|
27天前
|
存储 C语言
数据在内存中的存储方式
本文介绍了计算机中整数和浮点数的存储方式,包括整数的原码、反码、补码,以及浮点数的IEEE754标准存储格式。同时,探讨了大小端字节序的概念及其判断方法,通过实例代码展示了这些概念的实际应用。
57 1
|
1月前
|
存储
共用体在内存中如何存储数据
共用体(Union)在内存中为所有成员分配同一段内存空间,大小等于最大成员所需的空间。这意味着所有成员共享同一块内存,但同一时间只能存储其中一个成员的数据,无法同时保存多个成员的值。
|
28天前
|
存储 C语言
【c语言】字符串函数和内存函数
本文介绍了C语言中常用的字符串函数和内存函数,包括`strlen`、`strcpy`、`strcat`、`strcmp`、`strstr`、`strncpy`、`strncat`、`strncmp`、`strtok`、`memcpy`、`memmove`和`memset`等函数的使用方法及模拟实现。文章详细讲解了每个函数的功能、参数、返回值,并提供了具体的代码示例,帮助读者更好地理解和掌握这些函数的应用。
23 0
|
存储 程序员 C语言
程序员之路:C语言中存储类别
程序员之路:C语言中存储类别
135 0
|
1月前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
34 3