前言:

上一次我们总结了c语言的指针,做了指针的一些题目,接下来我们开始更加深入的讲解c语言的内存。

学习内存的必要性:

  1. 理解程序运行的本质
    程序的执行过程本质上是对数据的处理,而所有数据(变量、对象、函数等)都需要存储在内存中。学习内存可以让你明白:

    • 变量的值存在哪里?
    • 函数调用时参数和返回值如何传递?
    • 为什么局部变量在函数结束后会失效?
  2. 解决实际开发中的问题
    很多常见的 bug 都与内存直接相关:

    • 内存泄漏(程序持续占用内存不释放,导致系统变慢或崩溃)
    • 空指针异常(访问不存在的内存地址)
    • 缓冲区溢出(写入数据超过内存分配的大小,可能引发安全问题)
    • 野指针(访问已释放的内存)
  3. 优化程序性能
    内存操作的效率直接影响程序性能:

    • 频繁的内存分配 / 释放会消耗大量资源(比如 C++ 中的new/delete或 Python 中的垃圾回收)
    • 合理利用内存缓存(如 CPU 缓存)可以显著提升程序速度
    • 理解内存对齐机制能避免不必要的性能损耗
  4. 掌握底层语言特性
    对于 C/C++ 等直接操作内存的语言,内存管理是核心技能:

    • 堆(heap)和栈(stack)的区别决定了变量的生命周期
    • 指针的本质是内存地址,理解指针才能灵活操作数据结构
    • 动态内存分配让程序能根据运行时需求调整资源
  5. 应对高级场景
    在系统开发、嵌入式编程、高性能计算等领域,内存知识尤为关键:

    • 操作系统需要管理进程内存空间,防止冲突
    • 嵌入式设备内存有限,必须精打细算地使用资源
    • 数据库等系统需要设计高效的内存缓存策略

简单来说,内存就像程序的 “工作空间”,了解这个空间的规则,才能写出更稳定、高效、安全的代码。即使是有自动内存管理的语言,理解内存原理也能帮助你写出更优化的代码。

内存那些事儿:从存储表示到概念解析

在计算机世界里,内存是数据存储与程序运行的核心载体。今天,我们就结合思维导图,从计算机中数的表示(小端存储)内存的基本概念两方面,深入聊聊内存~

一、计算机中数的表示:小端存储下的花样表达

计算机以二进制存储数据,但不同类型的数(有无符号、浮点数等),存储规则大不相同,且遵循小端存储(数据的低位字节存在内存低地址处,高位字节存在内存高地址处)。

1. 整数的存储

整数分为有符号整数无符号整数,存储关键是对符号和数值位的处理。

  • 有符号整数
    • 采用补码存储。正数的补码与原码相同;负数的补码是原码除符号位外取反加 1。比如,假设是 8 位有符号数,-1的原码是10000001,补码就是11111111
    • 符号位(最高位)决定正负,0为正,1为负。数值位则表示数的大小,通过补码规则,能统一加减法运算(用加法电路即可处理减法)。
  • 无符号整数
    • 所有位都表示数值,没有符号位,所以只能表示非负整数。比如 8 位无符号数,范围是0~2550000000011111111)。
    • 若要表示负数相关逻辑,需手动模拟符号位等机制。
2. 浮点数的存储(IEEE 754 标准)

浮点数用于表示小数,遵循 IEEE 754 标准,分为float(单精度)和double(双精度),通过符号位、指数位、尾数位来存储。

  • IEEE 754 对有效数字M和指数E,还有一些特别规定。 前面说过,1≤M < 2,也就是说,M可以写成 1.xxxxxx的形式,其中xxxxxx表示小数部分。 IEEE 754 规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分。比如保存1.01的时 候,只保存01,等到读取的时候,再把第一位的1加上去。这样做的目的,是节省1位有效数字。以32位浮点数为例,留给M只有23位, 将第一位的1舍去以后,等于可以保存24位有效数字

  • float(32 位):
    • 符号位(1 位):0表示正,1表示负。
    • 指数位(8 位):采用偏移值为127的移码表示,实际指数 = 移码值 - 127
    • 尾数位(23 位):表示有效数字的小数部分,因为默认整数部分是1(归一化处理),所以实际精度是24位。
    • 例如,要存储1.5(二进制1.1),符号位0,指数位计算:1 + 127 = 128(二进制10000000),尾数位是100...0(因为0.1后面都是0)。
  • double(64 位):
    • 符号位(1 位)、指数位(11 位,偏移值1023)、尾数位(52 位,归一化后实际精度53位)。
    • 能表示的范围和精度比float更大、更高,但也存在浮点数精度问题(比如某些十进制小数无法精确用二进制表示)。
3. 特殊的 NaN 与 ±∞
  • NaN(Not a Number):当指数位全为1,且尾数位不全为0时,表示 “不是一个数”,用于处理如0/0这样的无效运算结果。
  • ±∞:指数位全为1,尾数位全为0,符号位为0表示+∞,为1表示-∞,用于表示如1/0这样的无穷大情况。

二、内存的概念:程序运行的 “舞台”

内存是程序运行时数据存储的区域,不同区域有不同的特性与用途。

1. 内存区域的基本划分与特性

程序运行时,内存大致可分为几个关键区域(以常见的程序内存布局为例):

  • 栈区(Stack)
    • 由编译器自动分配和释放,存储函数的参数、局部变量等。
    • 遵循 “先进后出” 原则,空间较小,操作效率高。比如函数调用时,会在栈上为局部变量开辟空间,函数结束后自动释放。
  • 堆区(Heap)
    • 由程序员手动分配和释放(如 C 语言的malloc/free,C++ 的new/delete),若不手动释放,可能导致内存泄漏。
    • 空间较大,可动态分配,但操作相对栈区更复杂,效率稍低。常用于存储动态创建的对象、数组等。
  • 全局 / 静态存储区
    • 存储全局变量和静态变量,程序运行期间一直存在,程序结束时由系统释放。
  • 代码区
    • 存储程序的二进制指令,只读,保证程序指令不被意外修改。
2. 内存操作的关键注意点
  • 内存分配与释放:堆区内存需手动管理,分配后若忘记释放,会造成内存泄漏;若释放后继续使用,会出现 “野指针” 问题。
  • 内存访问权限:不同区域的内存访问权限不同,比如代码区只读,若强行写入会触发错误。
  • 内存大小与对齐:不同平台、编译器下,内存的寻址和分配可能存在对齐要求,不注意可能导致性能问题或错误。

通过对 “数的存储(小端下的各类数表示)” 和 “内存本身概念” 的学习,能更清晰理解程序中数据的底层存储逻辑,以及内存这个 “大舞台” 是如何支撑程序运行的~

小杨在这里预祝各位事业飞黄腾达!

感谢浏览!

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐