当前位置: 首页 > news >正文

【嵌入式C语言】内存分布

内存分布

  • 内存分布图
    • 内存的属性:
    • 只读空间
      • 只读空间的特点
      • 编程注意事项
    • 栈空间
      • 栈的工作原理
      • 栈的特点
      • 栈溢出
      • 与堆的区别
    • 堆空间
      • 堆的特点
      • 内存分配函数
      • 内存泄漏
      • 总结


内存分布图

内存的属性:

在C语言中,内存的属性主要取决于它是如何分配的以及它在程序中的作用。我们可以将内存分为以下几个主要类别:

  1. 静态存储区(Static Storage Duration)

    • 静态变量和全局变量存储在这里。
    • 这些变量在程序启动时初始化,在程序结束时销毁。
    • 它们的生命周期是整个程序的执行过程。
    • 内存由编译器自动管理,程序员无需手动释放。
  2. 自动存储区(Automatic Storage Duration)

    • 函数内部定义的局部变量通常存储在此区域。
    • 这些变量在函数调用开始时创建,在函数结束时销毁。
    • 每次调用函数都会为这些变量分配新的空间。
  3. 动态存储区(Dynamic Storage Duration)

    • 通过malloccallocrealloc等函数分配的内存块。
    • 这些内存块可以在程序运行期间任何时间点分配或释放。
    • 程序员负责管理这部分内存,使用free函数来释放不再需要的内存以防止内存泄漏。
  4. 线程存储区(Thread Storage Duration, C11引入)

    • 使用_Thread_local关键字声明的变量存储在这里。
    • 这些变量为每个线程单独实例化一次,并且存在于线程的整个生命周期中。
  5. 常量存储区

    • 字符串字面量和其他常量数据通常存储在这里。
    • 这部分内存中的值不应该被修改(尝试修改可能导致未定义行为)。
  6. 堆栈(Stack)与堆(Heap)

    • 堆栈用于实现自动存储区,具有快速分配和释放的特点,因为它的操作遵循后进先出(LIFO)原则。
    • 堆用于实现动态存储区,允许更灵活的内存管理,但分配和释放速度相对较慢。

每种类型的内存都有其特定的用途和管理方式,理解它们的区别对于编写高效且无错误的C程序至关重要。此外,了解这些概念有助于避免常见的编程错误,如内存泄漏、悬空指针和缓冲区溢出等。

最重要的内存属性就是大小和位置

  1. 大小
  2. 在哪里

编译-汇编-链接-运行

链接就是把.o文件链接在一起,生成可执行文件,如下图所示,放到section中,就是放到各个段中

在这里插入图片描述

一个变量存放的位置不同,效果是否不同呢?

运行如下代码测试:

#include <stdio.h>
int main(int argc, char **argv[])
{int a = 0x10;printf("the a is %p\n", &a);printf("main  is %p\n", main);return 0;
}

输出结果如下,可以看到这两个地址相差很远,猜测应该在不同的段中~

the a is 000000000061FE1C
main  is 0000000000401550

修改成全局变量,看一下输出结果如何

#include <stdio.h>
int a = 0x10;
int main(int argc, char **argv[])
{printf("the a is %p\n", &a);printf("main  is %p\n", main);return 0;
}

输出结果如下,可以看到两个地址相差很小,猜测在同一个段中~

the a is 0000000000403010
main  is 0000000000401550

内存分布图

高地址
---------------------------------
内核空间(应用程序不可访问)
---------------------------------3G
栈空间(局部变量) RW
---------------------------------
运行时的堆空间(malloc分配的内存)
---------------------------------
全局数据空间(初始化的、未初始化的)static RW data bss
只读数据段(字符串常量、"Hello world!") R TEXT
代码段(code) R TEXT
---------------------------------
0x0;低地址

只读空间

只读空间是静态空间,整个程序结束时释放内存,生存周期最长。

代码段:存放程序代码,可读不可写,存放程序执行代码的二进制机器指令

修改代码段的内容,会导致程序崩溃,下面是示例程序

#include <stdio.h>
int a = 0x10;
int main()
{printf("the a is %p\n", &a);printf("main  is %p\n", main);unsigned char *p = (unsigned char *)main;printf("p[0] is %x\n", p[0]);p[0] = 0x90;printf("+++++++++p[0] is %x\n", p[0]);return 0;
}

win10的运行结果

the a is 0000000000403010
main  is 0000000000401550
p[0] is 55

后面的内容就输出不成功了,程序崩溃了;在linux系统中这里能提示出现了段错误。

在这里插入图片描述

在这里插入图片描述

在C语言中,只读空间(Read-Only Memory, ROM)通常用于存储程序运行期间不应该被修改的数据。这类数据包括但不限于:

  1. 字符串字面量:当你在代码中使用字符串如"Hello, world!"时,这些字符串常量会被存储在只读区域。

  2. 全局或静态常量:使用const关键字声明的全局变量或静态变量如果初始化了,则它们可能会被放置在只读段中。例如:

   const int MAX_SIZE = 100;
  1. 函数代码:编译后的机器指令一般也被存放在只读内存区域,以防止意外或恶意地修改程序的行为。

  2. 其他不可变数据结构:一些编译器和链接器可能会选择将特定类型的常量数据放入只读段,以优化程序性能和保护数据完整性。

只读空间的特点

  • 不可修改性:一旦程序加载,这部分内存中的内容不能被程序修改。任何尝试修改只读内存的行为都会导致未定义行为,这可能包括程序崩溃或安全漏洞。

  • 共享性:由于只读数据不会改变,多个进程可以共享同一块只读内存,从而节省内存资源。例如,当多个进程执行同一个程序时,它们可以共享该程序的代码部分。

  • 持久性:只读数据通常在程序启动时加载,并且在整个程序运行期间保持不变,直到程序结束。

编程注意事项

当你编写C程序时,应该避免尝试修改任何你认为是只读的数据。如果你需要修改一个看起来像是常量的东西,那么你应该确保它不是存储在只读内存中,或者创建一个可写的副本。此外,在某些嵌入式系统开发环境中,了解如何正确配置和使用只读内存对于优化性能和资源管理非常重要。


栈空间

程序运行时的空间,函数内部使用的变量,函数一旦返回,就释放了,生存周期(在函数内)较短。

  • 使用size命令查看可执行文件的大小
  • 局部变量,一旦返回之后,就失效了,访问不了了
  • 全局变量赋值之前放在bss段,赋值之后放在data段
  • static变量(没有初始化的),放在bss段。
  • 使用nm命令查看可执行文件中的符号表。 nm .\修改代码段.exe
PS E:\CProject\内存分布图\output> size .\修改代码段.exetext    data     bss     dec     hex filename9868    2244    2432   14544    38d0 .\修改代码段.exe

栈空间(Stack)是计算机内存中用于存储程序运行时数据结构的一个区域,具有后进先出(LIFO, Last In First Out)的操作特性。在C语言编程中,栈空间主要用于以下几种用途:

  1. 函数调用:每当一个函数被调用时,一个新的栈帧(也叫激活记录)会被创建并压入栈顶。这个栈帧包含了该函数的局部变量、参数以及返回地址等信息。

  2. 局部变量存储:在函数内部定义的自动变量(即没有使用static修饰符的变量)通常会被分配到栈上。这些变量在函数调用开始时创建,在函数结束时自动销毁。

  3. 保存寄存器状态:当函数调用发生时,为了保证调用者环境的安全,CPU寄存器的内容也会被保存到栈上,并在函数返回前恢复。

  4. 临时数据:编译器可能还会使用栈来存储中间计算结果或其他临时性数据。

栈的工作原理

  • 栈指针(Stack Pointer, SP):这是一个特殊的寄存器,指向当前栈顶的位置。
  • 压栈(Push):将数据项添加到栈顶。这会减少SP的值(在大多数架构中,栈是从高地址向低地址增长的)。
  • 弹栈(Pop):从栈顶移除数据项,并将其值赋予另一个位置或寄存器。这会增加SP的值。
  • 栈帧(Frame):每个函数调用都会创建一个新的栈帧,它包含该函数所需的所有信息,如局部变量、参数和返回地址。

栈的特点

  • 快速访问:由于栈的操作简单且直接(仅涉及栈顶),因此其操作速度非常快。
  • 自动管理:栈上的内存由编译器自动管理,程序员无需显式地释放内存。
  • 有限大小:栈的空间通常是有限的,如果超出这个限制就会导致栈溢出错误。例如,递归深度过大或者局部变量占用过多空间都可能导致这种情况。

栈溢出

栈溢出是指栈空间耗尽的情况,可能会导致程序崩溃或者其他未定义行为。常见的原因包括无限递归、过大的局部数组等。为了避免栈溢出,应该注意避免不必要的深递归调用,并谨慎处理大尺寸的局部变量。

与堆的区别

栈和堆都是程序用来动态分配内存的区域,但它们有着显著的不同:

  • 分配方式:栈是由编译器自动管理的,而堆需要程序员通过诸如malloccallocreallocfree等函数手动管理。
  • 生存期:栈上的对象在其对应的函数调用结束后自动消失;堆上的对象则持续存在直到显式释放。
  • 性能:由于栈的操作更简单,所以通常比堆更快。
  • 大小:一般而言,栈的大小相对较小,而堆可以扩展到系统可用内存的最大限度。

了解栈的工作机制对于编写高效、安全的C代码非常重要,尤其是在设计复杂的数据结构和算法时。


堆空间

程序运行时,可以自由,自我管理的分配和释放的空间。生命周期由程序员来决定。

分配:malloc,calloc,realloc,new

malloc();一旦分配成功,就返回分配好的地址给我们,只需要接收;对于这个新地址的读法,由程序员灵活把握,输入参数指定分配的大小,单位是B。

char *p;
p = (char *)malloc(10*sizeof(char));
if (p == NULL)
{// error
}

malloc函数的用法

#include <stdio.h>
#include <stdlib.h>
int main()
{int *p = (int *)malloc(10 * sizeof(int));if (p == NULL){printf("malloc failed\n");return -1;}
}

释放:free,delete

  • free 和 malloc是配套使用的,malloc分配的内存,一定要用free释放,否则会造成内存泄漏!!!
  • free();释放malloc分配的内存,释放之后,这块内存就可以被再次分配了。

堆空间(Heap)是内存中用于动态分配内存的一个区域,它允许程序在运行时请求和释放任意大小的内存块。与栈不同的是,堆的空间管理是由程序员负责的,并且它的内存分配不依赖于函数调用栈。以下是关于C语言中堆空间的一些重要信息:

堆的特点

  1. 动态内存分配:堆主要用于在程序运行期间动态地分配和释放内存。这意味着可以在程序执行过程中根据需要分配内存,而不是在编译时确定。

  2. 手动管理:与栈上的自动变量不同,堆上的内存分配和释放必须由程序员显式地进行。这提供了更大的灵活性,但也增加了管理和维护的复杂性。

  3. 生存期长:堆上分配的对象在其被显式释放之前一直存在。这意味着它们可以跨越多个函数调用或整个程序的生命周期。

  4. 碎片化问题:由于堆上的内存块可以以任意顺序分配和释放,因此可能会导致内存碎片化,即虽然有足够的总可用内存,但没有足够大的连续内存块来满足新的分配请求。

  5. 较慢的操作速度:相比栈操作,堆上的分配和释放通常更慢,因为涉及到更复杂的内存管理逻辑。

内存分配函数

C语言提供了几个标准库函数来处理堆上的内存分配和释放:

  • malloc(size_t size):分配指定字节数的内存,并返回指向这块内存的指针。如果分配失败,则返回NULL

  • calloc(size_t num, size_t size):为num个元素,每个元素占用size字节的内存分配空间,并将所有内容初始化为零。同样,在分配失败时返回NULL

  • realloc(void *ptr, size_t new_size):调整之前通过malloccallocrealloc分配的内存块的大小。它可以增大或缩小内存块。如果无法调整原内存块的大小,它会尝试分配新的内存块并将旧数据复制过去,然后释放旧的内存块。如果调整失败,则返回NULL,而原来的内存块保持不变。

  • free(void *ptr):释放之前分配的内存块。释放后的指针不应再被使用,否则会导致未定义行为。

内存泄漏

当程序分配了堆上的内存但忘记释放时,就会发生内存泄漏。这些未释放的内存块不能再被程序使用,从而浪费了系统资源。长时间运行的应用程序如果不正确管理堆内存,可能会逐渐消耗掉所有可用内存,最终导致性能下降甚至崩溃。

为了避免内存泄漏,应该确保每次调用malloccallocrealloc后,都有相应的free调用来释放不再需要的内存。此外,还可以使用一些工具和技术(如Valgrind等内存调试工具)来检测和预防内存泄漏。

总结

堆空间提供了一种灵活的方式来管理程序运行时所需的内存,但同时也要求程序员更加小心谨慎地管理内存的分配和释放,以避免常见的错误如内存泄漏、悬空指针等问题。理解如何有效地利用堆对于编写高效、可靠的C程序至关重要。

相关文章:

【嵌入式C语言】内存分布

内存分布 内存分布图内存的属性&#xff1a;只读空间只读空间的特点编程注意事项 栈空间栈的工作原理栈的特点栈溢出与堆的区别 堆空间堆的特点内存分配函数内存泄漏总结 内存分布图 内存的属性&#xff1a; 在C语言中&#xff0c;内存的属性主要取决于它是如何分配的以及它在…...

【brainpan靶场渗透】

文章目录 一、基础信息 二、信息收集 三、反弹shell 四、提权 一、基础信息 Kali IP&#xff1a;192.168.20.146 靶机 IP&#xff1a;192.168.20.155 二、信息收集 似乎开放了9999&#xff0c;10000端口&#xff0c;访问页面没有太多内容&#xff0c;扫描一下目录 dirs…...

Java实现观察者模式

一、前言 观察者模式&#xff0c;又称为发布订阅模式&#xff0c;是一种行为设置模式&#xff0c;允许对象之间建立一对多的依赖关系&#xff0c;这样当一个对象状态改变时&#xff0c;它的所有依赖者&#xff08;观察者&#xff09;都会收到通知并自动更新。 二、具体实现 …...

通过百度api处理交通数据

通过百度api处理交通数据 1、读取excel获取道路数据 //道路名称Data EqualsAndHashCode public class RoadName {ExcelProperty("Name")private String name; }/*** 获取excel中的道路名称*/private static List<String> getRoadName() {// 定义文件路径&…...

探索CSDN博客数据:使用Python爬虫技术

探索CSDN博客数据&#xff1a;使用Python爬虫技术 在数字化的浪潮中&#xff0c;数据的获取与分析变得日益关键。CSDN作为中国领先的IT社区和服务平台&#xff0c;汇聚了海量的技术博客与文章&#xff0c;成为一座蕴藏丰富的数据宝库。本文将引领您穿梭于Python的requests和py…...

b站ip属地评论和主页不一样怎么回事

在浏览B站时&#xff0c;细心的用户可能会发现一个有趣的现象&#xff1a;某些用户的评论IP属地与主页显示的IP属地并不一致。这种差异引发了用户的好奇和猜测&#xff0c;究竟是什么原因导致了这种情况的发生呢&#xff1f;本文将对此进行深入解析&#xff0c;帮助大家揭开这一…...

如何查看服务器内存占用情况?

如何查看服务器的内存占用情况&#xff1f;你知道内存使用情况对服务器性能的重要性吗&#xff1f;内存是服务器运行的核心资源之一&#xff0c;了解内存的占用情况可以帮助你优化系统性能。 要查看服务器的内存占用情况&#xff0c;首先需要确定你使用的是哪种操作系统。不同…...

流架构的读书笔记(2)

流架构的读书笔记&#xff08;2&#xff09; 一、建模工具之一沃德利地图 推测技术的发展,交流和辩论思想的最有力的方法是沃德利地图 沃德利地图的制作步骤 1确定范围和用户需求 2确定满足用户需求所需的组件 3在一条范围从全新到被人们接受的演进轴上评估这些组成 部分的演…...

E6 中的 扩展运算符(Spread) 和 剩余运算符(Rest)

时间&#xff1a;2024.12.29 之前看到 Es6 中的 三点运算符&#xff0c;有如下的几种写法&#xff0c;有时候三点运算符放在左边&#xff0c;有时候三点运算符放在右边&#xff0c;老是混淆。今天记录下&#xff0c;加强理解。 先看一个问题 最近在看 《ECMAScript 6 入门》关于…...

Python的简单爬虫框架

爬虫为网络爬虫&#xff08;又称为网页蜘蛛&#xff0c;网络机器人&#xff0c;在FOAF社区中间&#xff0c;更经常的称为网页追逐者&#xff09;&#xff0c;是一种按照一定的规则&#xff0c;自动地抓取万维网信息的程序或者脚本。另外一些不常使用的名字还有蚂蚁、自动索引、…...

使用 uni-app 开发的微信小程序中,如何在从 B 页面回来时,重新拉取数据?

&#x1f468;&#x1f3fb;‍&#x1f4bb; 热爱摄影的程序员 &#x1f468;&#x1f3fb;‍&#x1f3a8; 喜欢编码的设计师 &#x1f9d5;&#x1f3fb; 擅长设计的剪辑师 &#x1f9d1;&#x1f3fb;‍&#x1f3eb; 一位高冷无情的全栈工程师 欢迎分享 / 收藏 / 赞 / 在看…...

Windows API Set:那些“只存在但不被使用“的DLL

API Set 是什么&#xff1f; 想象一下&#xff0c;Windows就像一个大型图书馆&#xff0c;而API Set就是这个图书馆的索引系统。但这个索引系统非常特别&#xff1a;它是直接内置在Windows加载器中的"虚拟目录"。 // 一个典型的API Set映射示例 api-ms-win-core-mem…...

黑神话悟空鼠标光标分享

效果图&#xff1a; 鼠标光标特点 这套鼠标光标的设计灵感来源于《黑神话&#xff1a;悟空》游戏中的角色和元素&#xff0c;具有以下特点&#xff1a; • 主题鲜明&#xff1a;光标设计紧扣游戏主题&#xff0c;采用了游戏中的元素&#xff0c;让玩家在使用电脑时也能感受到…...

编写一个简单的引导加载程序(bootloader)

编写一个简单的引导加载程序&#xff08;bootloader&#xff09;通常用于嵌入式系统或自定义操作系统。这里&#xff0c;我将为你提供一个基于x86架构的简单汇编语言 bootloader 示例。这个 bootloader 将会在启动时打印一条消息到屏幕上。 使用 NASM 汇编器来编写这个 bootlo…...

【Linux基础】进程(上) —— 概念、状态、优先级与环境变量

目录 一、进程的概念 1. 什么是进程 PCB进程控制块的理解 2. 查看进程的方式 ps ajx 指令 getpid系统调用 3. 另外一种查看进程的方式(了解) 4. 进程的常见调用 fork 创建子进程 现象说明 二、进程的状态 1. 操作系统层面的进程状态 ① 运行状态 ② 阻塞状态 ③…...

Rust: enum 和 i32 的区别和互换

在Rust编程语言中&#xff0c;enum&#xff08;枚举&#xff09;和i32是两种不同类型的数据结构&#xff0c;它们各自有不同的用途和特性。 i32 i32是一个32位的有符号整数类型。它用于存储整数值&#xff0c;范围从-2,147,483,648到2,147,483,647。i32是Rust中的基本数据类型…...

2024年终回顾

前言 很久没有更新博客&#xff0c;因为工作内容主要是内场开发&#xff0c;后来有点和互联网脱轨&#xff0c;断断续续上来看一下。这个总结应该也很简单&#xff0c;涉及以下的几个内容进行逐一说明 一、就业问题 这个问题可能很尖锐&#xff0c;从大环境来说&#xff0c;去…...

RGB、HSV颜色模型及MATLAB互换应用实例

一、前言 RGB和HSV模型是数字图像处理中颜色空间中的两种重要表示方式&#xff0c;RGB和HSV都是描述颜色的数学模型&#xff0c;可以用于表示和处理图像中的颜色信息。 RGB模型是一种基于光的颜色模型&#xff0c;由红&#xff08;Red&#xff09;、绿&#xff08;Green&#x…...

# 【超全面了解鸿蒙生命周期】-生命周期补充

【超全面了解鸿蒙生命周期】-生命周期补充 鸿蒙所有的生命周期函数梳理 文章目录 【超全面了解鸿蒙生命周期】-生命周期补充前言一、AbilityStage的生命周期二、ExtensionAbility卡片生命周期三、Web组件常用生命周期 前言 本文是继之前写的生命周期函数梳理的进一步补充&…...

黑神话悟空游戏鼠标光标使用教程与下载

效果图&#xff1a; 鼠标光标特点 这套鼠标光标的设计灵感来源于《黑神话&#xff1a;悟空》游戏中的角色和元素&#xff0c;具有以下特点&#xff1a; • 主题鲜明&#xff1a;光标设计紧扣游戏主题&#xff0c;采用了游戏中的元素&#xff0c;让玩家在使用电脑时也能感受到…...

【机器学习】梯度下降

文章目录 1. 梯度下降概念2. 梯度下降的技巧2.1 动态设置学习率2.2 Adagrad调整梯度2.3 随机梯度下降&#xff08;SGD&#xff09;2.4 特征缩放 3. 梯度下降理论基础 1. 梯度下降概念 梯度&#xff1a;Loss 对参数在某一点的偏微分&#xff0c;函数沿梯度的方向具有最大的变化…...

【leetcode 07】707.设计链表

要点⭐ 链表的常见操作 获取第n个节点的值 头部插入节点 尾部插入节点 第n个节点前插入&#xff08;先立新&#xff0c;在破旧&#xff09; 删除第n个节点 class ListNode{int val;ListNode next;ListNode(){};ListNode(int val){this.valval;} } class MyLinkedList {//链表大…...

【Spring】详解(上)

Spring 框架核心原理与应用&#xff08;上&#xff09; 一、Spring 框架概述 &#xff08;一&#xff09;诞生背景 随着 Java 应用程序规模的不断扩大以及复杂度的日益提升&#xff0c;传统的 Java开发方式在对象管理、代码耦合度等方面面临诸多挑战。例如&#xff0c;对象之…...

短视频矩阵系统后端源码搭建实战与技术详解,支持OEM

一、引言 随着短视频行业的蓬勃发展&#xff0c;短视频矩阵系统成为了众多企业和创作者进行多平台内容运营的有力工具。后端作为整个系统的核心支撑&#xff0c;负责处理复杂的业务逻辑、数据存储与交互&#xff0c;其搭建的质量直接影响着系统的性能、稳定性和可扩展性。本文将…...

力扣每日刷题

24. 两两交换链表中的节点 - 力扣&#xff08;LeetCode&#xff09; 递归写法 做题思路&#xff1a;把需要交换的两个数的前一个数作为参数传入&#xff0c;然后使用一个变量保存这两个变量的后一个数&#xff0c;交换这个两个数&#xff0c;最后把第二个数&#xff08;原第一…...

QT的信号和槽页面的应用

完善对话框&#xff0c;点击登录弹出对话框&#xff0c;如果账号和密码匹配&#xff0c;则弹出信息对话框&#xff0c;给出提示”登录成功“&#xff0c;提供一个Ok按钮&#xff0c;用户点击Ok后&#xff0c;关闭登录界面&#xff0c;跳转到其他界面 如果账号和密码不匹配&…...

【Java】线程相关面试题 (基础)

文章目录 线程与进程区别并行与并发区别解析概念含义资源利用执行方式应用场景 创建线程线程状态如何保证新建的三个线程按顺序执行wait方法和sleep方法的不同所属类和使用场景方法签名和参数说明调用wait方法的前提条件被唤醒的方式与notify/notifyAll方法的协作使用示例注意事…...

【数字化】华为一体四面细化架构蓝图

导读&#xff1a;华为的“一体四面”企业架构设计方法是一种综合性的管理框架&#xff0c;它通过业务架构、信息架构、应用架构和技术架构的集成设计&#xff0c;构建出一个既符合业务需求&#xff0c;又具备高度灵活性和可扩展性的IT系统。这种架构设计方法强调从业务视角出发…...

frameworks 之 WMS添加窗口流程

frameworks 之 触摸事件窗口查找 1.获取WindowManager对象2.客户端添加view3. 服务端添加view (NO_SURFACE)4.重新布局 (DRAW_PENDING)4.1 创建 SurfaceControl 5.通知绘制 (COMMIT_DRAW_PENDING&#xff0c; READY_TO_SHOW&#xff0c; HAS_DRAWN)5. 1 布局测量和刷新 6.总结 …...

搜索方法归类全解析

搜索方法归类全解析 搜索方法是人工智能和计算机科学中用于解决问题、优化路径或发现数据模式的关键技术。根据不同的标准&#xff0c;搜索方法可以被分为多种类别。本文将详细介绍这些分类标准&#xff0c;并探讨每一类的特点及其代表算法&#xff0c;同时补充更多关于搜索的相…...