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 语言中,存储类定义了变量的生命周期、作用域和可见性。主要包括:`auto`(默认存储类,块级作用域),`register`(建议存储在寄存器中,作用域同 `auto`,不可取地址),`static`(生命周期贯穿整个程序,局部静态变量在函数间保持值,全局静态变量限于本文件),`extern`(声明变量在其他文件中定义,允许跨文件访问)。此外,`typedef` 用于定义新数据类型名称,提升代码可读性。 示例代码展示了不同存储类变量的使用方式,通过两次调用 `function()` 函数,观察静态变量 `b` 的变化。合理选择存储类可以优化程序性能和内存使用。
133 82
|
6天前
|
存储 人工智能 C语言
数据结构基础详解(C语言): 栈的括号匹配(实战)与栈的表达式求值&&特殊矩阵的压缩存储
本文首先介绍了栈的应用之一——括号匹配,利用栈的特性实现左右括号的匹配检测。接着详细描述了南京理工大学的一道编程题,要求判断输入字符串中的括号是否正确匹配,并给出了完整的代码示例。此外,还探讨了栈在表达式求值中的应用,包括中缀、后缀和前缀表达式的转换与计算方法。最后,文章介绍了矩阵的压缩存储技术,涵盖对称矩阵、三角矩阵及稀疏矩阵的不同压缩存储策略,提高存储效率。
|
8天前
|
存储 算法 C语言
数据结构基础详解(C语言): 二叉树的遍历_线索二叉树_树的存储结构_树与森林详解
本文从二叉树遍历入手,详细介绍了先序、中序和后序遍历方法,并探讨了如何构建二叉树及线索二叉树的概念。接着,文章讲解了树和森林的存储结构,特别是如何将树与森林转换为二叉树形式,以便利用二叉树的遍历方法。最后,讨论了树和森林的遍历算法,包括先根、后根和层次遍历。通过这些内容,读者可以全面了解二叉树及其相关概念。
|
8天前
|
存储 机器学习/深度学习 C语言
数据结构基础详解(C语言): 树与二叉树的基本类型与存储结构详解
本文介绍了树和二叉树的基本概念及性质。树是由节点组成的层次结构,其中节点的度为其分支数量,树的度为树中最大节点度数。二叉树是一种特殊的树,其节点最多有两个子节点,具有多种性质,如叶子节点数与度为2的节点数之间的关系。此外,还介绍了二叉树的不同形态,包括满二叉树、完全二叉树、二叉排序树和平衡二叉树,并探讨了二叉树的顺序存储和链式存储结构。
|
9天前
|
C语言
C语言程序设计核心详解 第二章:数据与数据类型 4种常量详解 常见表达式详解
本文详细介绍了C语言中的数据与数据类型,包括常量、变量、表达式和函数等内容。常量分为整型、实型、字符型和字符串常量,其中整型常量有十进制、八进制和十六进制三种形式;实型常量包括小数和指数形式;字符型常量涵盖常规字符、转义字符及八进制、十六进制形式;字符串常量由双引号括起。变量遵循先定义后使用的规则,并需遵守命名规范。函数分为标准函数和自定义函数,如`sqrt()`和`abs()`。表达式涉及算术、赋值、自增自减和逗号运算符等,需注意运算符的优先级和结合性。文章还介绍了强制类型转换及隐式转换的概念。
|
8天前
|
存储 算法 C语言
C语言手撕数据结构代码_顺序表_静态存储_动态存储
本文介绍了基于静态和动态存储的顺序表操作实现,涵盖创建、删除、插入、合并、求交集与差集、逆置及循环移动等常见操作。通过详细的C语言代码示例,展示了如何高效地处理顺序表数据结构的各种问题。
|
14天前
|
存储 大数据 C语言
C语言 内存管理
本文详细介绍了内存管理和相关操作函数。首先讲解了进程与程序的区别及进程空间的概念,接着深入探讨了栈内存和堆内存的特点、大小及其管理方法。在堆内存部分,具体分析了 `malloc()`、`calloc()`、`realloc()` 和 `free()` 等函数的功能和用法。最后介绍了 `memcpy`、`memmove`、`memcmp`、`memchr` 和 `memset` 等内存操作函数,并提供了示例代码。通过这些内容,读者可以全面了解内存管理的基本原理和实践技巧。
|
14天前
|
缓存 Linux C语言
C语言 多进程编程(六)共享内存
本文介绍了Linux系统下的多进程通信机制——共享内存的使用方法。首先详细讲解了如何通过`shmget()`函数创建共享内存,并提供了示例代码。接着介绍了如何利用`shmctl()`函数删除共享内存。随后,文章解释了共享内存映射的概念及其实现方法,包括使用`shmat()`函数进行映射以及使用`shmdt()`函数解除映射,并给出了相应的示例代码。最后,展示了如何在共享内存中读写数据的具体操作流程。
|
存储 程序员 C语言
程序员之路:C语言中存储类别
程序员之路:C语言中存储类别
129 0
|
5天前
|
存储 Serverless C语言
【C语言基础考研向】11 gets函数与puts函数及str系列字符串操作函数
本文介绍了C语言中的`gets`和`puts`函数,`gets`用于从标准输入读取字符串直至换行符,并自动添加字符串结束标志`\0`。`puts`则用于向标准输出打印字符串并自动换行。此外,文章还详细讲解了`str`系列字符串操作函数,包括统计字符串长度的`strlen`、复制字符串的`strcpy`、比较字符串的`strcmp`以及拼接字符串的`strcat`。通过示例代码展示了这些函数的具体应用及注意事项。

热门文章

最新文章