首页
题库
公司真题
专项练习
面试题库
在线编程
面试
面试经验
AI 模拟面试
简历
求职
学习
基础学习课
实战项目课
求职辅导课
专栏&文章
竞赛
我要招人
发布职位
发布职位、邀约牛人
更多企业解决方案
AI面试、笔试、校招、雇品
HR免费试用AI面试
最新面试提效必备
登录
/
注册
一勺西瓜
重庆邮电大学 嵌入式软件开发
发布于重庆
关注
已关注
取消关注
@keke小牛:
嵌入式八股之c语言内存管理相关
由gcc编译的C语言程序占用的内存分为哪几个部分映射区存储动态链接以及mmap函数进行的文件映射堆区提供程序员动态申请的空间全局(静态区)存放全局变量和静态变量,已初始化的全局变量和静态变量、const型常量在一块区域(.data段),未初始化的、初始化为0的全局变量和静态局部变量在相邻的另一块区域(.bss段)常量区字符串、数字等常量存放在常量区。const修饰的全局变量存放在常量区程序代码区存放函数的二进制代码和字符常量代码段和数据段属于静态内存,堆栈属于动态内存静态内存的特点:各个变量的地址在编译期间就确定了,在程序运行中不再改变动态内存的特点:在程序运行期间内存不固定在可执行文件内不给BSS段分配存储空间,在 程序运行内存时再分配存储空间和地址。堆区和栈区栈区栈有两种基本操作:入栈(push)和出栈(pop)。入栈是把一个 栈元素压入栈中,而出栈则是从栈中弹出一个栈元素。入栈和出栈都 靠栈指针(Stack Pointer,SP)来维护,SP会随着入栈和出栈在栈顶上下移动C语言函数中的局部变量、传递的实参、 返回的结果、编译器生成的临时变量都是保存在栈中的。在很多嵌入式系统的启动代码中,系统 一上电开始运行的都是汇编代码,在跳到第一个C语言函数运行之前, 都要先初始化栈空间。栈的初始化其实就是栈指针SP的初始化,在Linux环境下,栈的起始地址一般就 是进程用户空间的最高地址,紧挨着内核空间,栈指针从高地址往低 地址增长Linux默认给每一个用户进程栈分配8MB大小的空间每一个函数都会有自己专门的栈空间来保存这些 数据,每个函数的栈空间都被称为栈帧(Frame Pointer,FP)。每一 个栈帧都使用两个寄存器FP和SP来维护,FP指向栈帧的底部,SP指向 栈帧的顶部。上一级函 数栈帧的起始地址,即栈底也会保存到当前函数的栈帧中,多个栈帧 通过FP构成一个链,这个链就是某个进程的函数调用栈。很多调试器 支持回溯功能,其实就是基于这个调用链来分析函数的调用关系的。当要传递的参数个数小于4时,直接使用R0~R3寄存 器传递即可;当要传递的参数个数大于4时,前4个参数使用寄存器传 递,剩余的参数则压入堆栈保存。参数传递时按照从右到左的顺序 依次压入堆栈.堆区堆是Linux进程空间中一片可 动态扩展或缩减的内存区域,一般位于BSS段的后面。堆内存是匿名的,不能像变量那样使用名字直接访问,一般通 过指针间接访问。在函数运行期间,对函数栈帧内的内存访问也不能像变量那样 通过变量名直接访问,一般通过栈指针FP或SP相对寻址访问。堆内存由程序员自己申请和释放,函数退出时,如果程序员没 有主动释放,就会造成内存泄漏。在裸机环境下一片连续的堆内存空间,经过多次小块内存的申请 和释放后,就会造成内存碎片化,malloc()/free()函数的底层实现,其实就是通过系统调用brk向 内核的内存管理系统申请内存。内核批准后,就会在BSS段的后面留出 一片内存空间,允许用户进行读写操作大于128KB,一般会通过mmap系 统调用直接映射一片内存,使用结束后再通过ummap系统调用归还这块 内存堆内存分配当用户申请一块内存时,内存分配器就根据申请的内存大小 从 bins 查 找 合 适 的 内 存 块 。 当 申 请 的 内 存 块 小 于 M_MXFAST 时 , ptmalloc分配器会首先到fast bins中去看看有没有合适的内存块,如 果没有找到,则再到small bins中查找如果要申请的内存块大于512 字节,则直接跳过small bins,直接到unsorted bin中查找内存分配器如果在unsorted bin中没有找到合适 大小的内存块,则会将unsorted bins中物理相邻的内存块合并,根据 合并后的内存块大小再迁移到small bins或large bins中防止栈溢出尽量不要在函数内使用大数组,如果确实需要大块内存,则可 以使用malloc申请动态内存。函数的嵌套层数不宜过深。递归的层数不宜太深。堆区和栈区主要区别:• 申请方式:栈区内存由系统自动分配和释放,函数结束时释放;堆区内存由程序员自己申请,并指明大小,用户忘释放时,会造成内存泄露,不过进程结束时会由系统回收• 申请后系统的响应:栈只要有剩余空间就能提供内存,否则报异常;堆则要先在自由链表中找到足够大的空间再分配给程序 。堆区和栈区出现数组越界,各自的表现如下:• 栈上数组越界:可能会覆盖其他变量或者返回地址等重要信息,导致程序崩溃或者被攻击。• 堆上数组越界:可能会破坏堆管理器的数据结构,导致后续分配或者释放出错或者失败。• 空间大小:栈区空间通常有一个限制,比如8MB;堆区空间一般比较大,受限于操作系统的有效虚拟内存。• 碎片问题:栈使用是先进后出的顺序,不会产生碎片;堆频繁地分配和释放可能会造成碎片,使得效率降低。在堆区分别申请两个1个字节的内存,其内存空间不一定会连续。因为堆区的内存分配是在逻辑地址上是连续的,但在物理地址上是不连续的 。堆区的内存分配还要考虑自由链表中是否有足够大的空间 。如果没有,可能会导致分配失败或者产生碎片。• 生长方向:栈向低地址扩展;堆向高地址扩展。当在堆区和栈区出现数组越界时,它们表现出的情况有所不同。堆区:运行时错误(Runtime Error):在堆区动态分配的数组,如果越界访问了数组元素,通常不会导致直接的崩溃或错误。这是因为在堆区分配的内存是由程序员负责管理的,操作系统不会对越界进行保护。程序可能会继续执行,但是访问到越界的内存可能导致不可预测的行为,可能会覆盖其他内存区域的内容,导致程序在后续执行中出现错误。内存损坏或泄漏:当在堆区进行数组越界访问时,可能会修改或破坏堆区中其他动态分配的内存块,导致内存损坏。另外,如果越界访问导致程序丢失对已分配内存的指针,可能会导致内存泄漏,因为程序无法正确释放这部分内存。栈区:栈溢出(Stack Overflow):在栈区分配的数组,如果越界访问了数组元素,可能会导致栈溢出。栈区是由编译器自动管理的,通常有固定的大小限制。当递归调用层数过多或者在函数内部使用大型数组时,栈的空间可能会耗尽,导致栈溢出错误,进而导致程序崩溃。运行时错误(Runtime Error):与堆区类似,栈区中的数组越界访问不会直接导致程序崩溃。程序可能会继续执行,但是访问到越界的内存可能导致不可预测的行为,因为栈区的越界访问也会影响到栈帧中其他的局部变量和返回地址等。在遇到数组越界问题时,不同编程语言和环境可能表现不同,有些语言可能会提供一定的越界检查机制,而有些则不会。为了避免这类问题,程序员应该在编程时注意数组的边界,并确保进行正确的边界检查,以保证程序的稳定性和安全性。程序a调用程序b ,栈有什么变化当程序a调用程序b时,栈会发生变化。程序a的栈帧会被压入栈中,然后程序b的栈帧被压入栈中。在程序b执行完后,程序b的栈帧会被弹出,然后程序a的栈帧也会被弹出。在调用函数时,参数和返回地址都会被压入栈中。函数执行完后,返回地址会被弹出,然后函数的返回值也会被压入栈中。堆栈溢出一般是由什么原因导致的?堆栈溢出一般包括堆内存溢出和栈内存溢出,两者都属于缓冲区溢出。堆内存溢出可能是堆的尺寸设置得过小/动态申请的内存没有释放。栈内存溢出可能是栈的尺寸设置得过小/递归层次太深/函数调用层次过深/分配了过大的局部变量。静态链接和动态链接静态链接如果我们在项目中引用了库函数, 则在编译时,链接器会将我们引用的函数代码或变量,链接到可执行 文件里,和可执行程序组装在一起,这种库被称为静态库缺点1: 如果在一个源 文件中我们定义了100个函数,而只使用了其中的1个,那么链接器在 链接时也会把这100个函数的代码指令全部组装到可执行文件中,这会 让最终生成的可执行文件体积大大增加,解决办法:我们在封装函数库时,将每个函数都 单独使用一个源文件实现,然后将多个目标文件打包即可缺点2:如C标准库里的printf()函数, 可能多个程序都调用了它,链接器在链接时就要将printf的指令添加 到多个可执行文件中。在一个多任务环境中,当多个进程并发运行 时,你会发现内存中有大量重复的printf指令代码,很浪费内存资源动态链接动态库在编译阶段不参与链接,不会和可执行文件组 装在一起,而是在程序运行时才被加载到内存参与链接,因此又叫作 动态链接库。优点:节省了内存资源:加载到内存的动态链接库可 以被多个运行的程序共享,使用动态链接可以运行更大的程序、更多 的程序,升级也更加简单方便。在Linux环境下,当我们运行一个程序时,操作系统首先会给程序 fork一个子进程,接着动态链接器被加载到内存,操作系统将控制权 交给动态链接器,让动态链接器完成动态库的加载和重定位操作,最 后跳转到要运行的程序。动态链接器在C标准库中实现,是glibc的一 部分,主要完成程序运行前的动态链接工作。动态链接器本身也是一个动态库, 即/lib/ld-linux.so文件。动态链接器被加载到内存后,会首先给自 己重定位,然后才能运行。像这种自己给自己重定位然后自动运行的 行为,我们一般称为自举动态链接需要考虑的一个重要问题是加载地址。动态链接库的地址要根据进程地址空间 的实际空闲情况随机分配。很容易想到的一个方法就是装载时重定位。想让我们的动态库放到内存的任何位置都可以运行,都可以被多个进程共享一种比较好的方法是将我们的动态库设计成与地址无关的代码,将指令中需要修改的部分(如对绝对地址符号的引用)分离出来,剩余的部分就和地址无关了,放到哪里都可以执行。以ARM平台为例,可 以采用相对寻址来实现延迟绑定:程序在 运行时,并不急着把所有的动态库都加载到内存中并进行重定位。当 动态库中的函数第一次被调用到时,才会把用到的动态库加载到内存 中并进行重定位问题1:但是当动态库作为第三方模块被不 同的应用程序引用时,库中的一些绝对地址符号(如函数名)将不可避免地被多次调用,需要重定位。动态库中的这些绝对地址符号,如何能做到同时被不同的应用程序引用解决办法:每个应用程序将引用的 动态库(绝对地址)符号收集起来,保存到一个表中,程序在运行过程中需要引用这些符号时, 可以通过这个表查询各个符号的地址,这个表为全局偏移表(GOT)。根据动态 库被加载到内存中的具体地址,更新GOT表中的各个符号(函数)的地 址。等下次该符号被引用时,程序可以直接跳到GOT表查询该符号的地 址,如果找到要调用的函数在内存中的实际地址,就可以直接跳过去 执行了动态链接和静态链接,分别用在哪些场景下1. 静态链接(Static Linking):在静态链接中,链接器将所有需要的目标文件和库文件的代码合并到一个单独的可执行文件中。这意味着可执行文件独立于系统上的其他库文件,它包含了所有程序运行所需的代码和数据。当程序被静态链接后,它可以在其他没有相关依赖的系统上运行,因为所有依赖项都已包含在可执行文件中。适用场景:独立可执行文件:静态链接生成的可执行文件可以在目标系统上独立运行,无需依赖其他外部库文件。这对于需要分发给其他用户或部署到不同环境的应用程序很有用。性能优化:静态链接可以在编译时将所有库文件和目标文件合并,这可能会提高程序的执行性能,因为在运行时不需要再进行动态加载和链接。版本控制:使用静态链接可以确保应用程序使用特定版本的库,减少由于不同版本库文件导致的不兼容性问题。2. 动态链接(Dynamic Linking):在动态链接中,程序在运行时仅保留对外部库函数的引用,并没有将实际代码合并到可执行文件中。相反,程序在运行时会从系统共享库(动态链接库或共享对象文件)中加载所需的函数。这意味着同一共享库可以被多个程序共享,节省内存空间,并且库的更新只需要替换共享库文件而不需要重新编译程序。适用场景:共享库的使用:动态链接适用于使用大型通用库的情况,这样多个程序可以共享同一个库,减少内存占用和二进制文件大小。库的更新和维护:动态链接使得库的更新更加方便,只需替换共享库文件,而不需要重新编译所有使用该库的程序。节省内存空间:多个程序使用同一个共享库时,动态链接可以节省内存空间,因为共享库的代码只需要在内存中加载一次。总体而言,静态链接适用于需要独立分发的程序或对性能优化有较高要求的场景,而动态链接适用于共享大型通用库、节省内存和方便库的更新维护的场景。编译时出现ld的报错,问题出在哪里未找到目标文件:链接器无法找到需要合并的目标文件。请确保所有源文件都已正确编译为目标文件,并在编译命令中正确指定它们的路径。重复定义符号:如果多个目标文件中出现了同名的全局变量或函数,链接器会报重复定义的错误。解决方法是确保每个符号只定义一次,并使用extern关键字声明其他地方引用的全局变量或函数。缺少依赖库:如果程序依赖于外部库,但链接器无法找到所需的库文件或库函数,将会报错。此时,您可以通过在编译命令中添加库文件路径或使用-l参数指定库来解决问题。缺少主函数:如果程序缺少main函数作为入口点,链接器将无法生成可执行文件。请确保每个源文件中都包含一个名为main的函数作为程序的入口。内存泄漏没有使用free()函数及时 地 将 这 块 内 存 归 还 给 内 存 分 配 器 ptmalloc 或 内 存 管 理 子 系 统 , ptmalloc和内存管理子系统就失去了对这块内存的控制权失去管理和追踪的这块内存,一直孤零零地躺在内存的某片区域,用户、内存分配器和内存管 理子系统都不知道它的存在,它就像内存中的一块漏洞,我们称这种 现象为内存泄漏。泄漏原因使用malloc,realloc函数后,没有通过free函数将内存释放掉内存泄漏检测:Mtrace它通过跟踪内存的使用记录 来动态定位用户代码中内存泄漏的位置。使用MTrace很简单,在代码 中添加下面的接口函数就可以了#include<mcheck.h>void mtrace(void);void muntrace(void);//mtrace()函数用来开启内存使用的记录跟踪功能,muntrace()函 数用来关闭内存使用的记录跟踪功能通过生成的日志文件mtrace.log来定位内存泄漏在程序中的位 置Linux内核模块运行机制而内核模块的运行不依赖C标准库,动态链接、重定 位过程需要内核自己来完成当我们使用insmod命令加载一个内核模块时,基本流程如下(1)kernel/module.c/init_module.(2)复制到内核:copy_module_from_user。(3)地址空间分配:layout_and_allocate。(4)符号解析:simplify_symbols。(5)重定位:apply_relocations。(6)执行:complete_formation。malloc和new的区别语法不同:malloc是C语言中的函数,需要手动指定分配内存的大小,并且返回的是void*类型的指针;而new是C++中的运算符,可以根据类型自动计算分配内存的大小,并且返回的是具体类型的指针。构造函数的调用:使用malloc分配的内存只是简单的分配了一块内存空间,不会调用对象的构造函数;而使用new分配的内存会调用对象的构造函数,完成对象的初始化。内存分配失败处理:malloc在内存分配失败时会返回NULL,需要手动判断是否分配成功;而new在内存分配失败时会抛出std::bad_alloc异常,可以使用try-catch来捕获异常。内存释放方式不同:malloc分配的内存必须使用free函数手动释放;而new分配的内存可以使用delete运算符来释放,同时会调用对象的析构函数来完成资源的释放。
点赞 4
评论 0
全部评论
推荐
最新
楼层
暂无评论,快来抢首评~
相关推荐
05-27 22:01
腾讯_HR(准入职员工)
腾讯云智研发内推-腾讯云智研发内推
真实体验是有超好的导师制定成长计划,全程辅导,各种腾讯内部学习网站和资料,上下班班车接送,然后基本一月团建一次。工作压力中等,百分之70情况能6点多下班,其他情况一般在8点左右。早投递,早筛选,早拿offer.!!!敲重点 用我的内推码投递后一定要评论区留言mark一下,以后好找我查进度,我秋招就是随便填别人的内推码,后来查进度都不知道找谁。惨痛的经历。#腾讯集团旗下|云智研发公司25届春招补录&26届暑期实习开始!【公司简介】云智研发公司是腾讯旗下的子公司,公司坚持投资区域书,布局研发人才,聚集云和智慧产业基础产品和行业标准产昂的研发。推进云与产业互联网战略落地,助力产业数字化转型升...
投递腾讯音乐娱乐集团等公司6个岗位 >
点赞
评论
收藏
分享
05-27 09:07
华中科技大学高等技术学院
华为or海康
本人是图像算法方向,暑期实习报了一个华为成都的ict无线,报了一个海康的杭州研究院智能算法。目前海康已开,华为面试已过还在泡,海康只有两天的考虑时间,要不要接受海康,还是继续等华为呢?希望在两边实习过的过来人能给点建议。
投递海康威视等公司8个岗位 >
点赞
评论
收藏
分享
05-27 13:17
门头沟学院 测试工程师
一投简历就想骂人正常吗
得了一种一投简历就想骂人的病春招找工作到现在的心情已经不知道怎么说了从一有面试会高兴 进度跟进会开心到后来被泡池子拿捏的难受心情的不断起起伏伏 时而焦虑时而躺平自我安慰到现在已经完全毫无波澜 甚至不想投简历一投简历就想骂人现在环境说真的已经太糟糕了承受着低薪奋力入行 还要在外地租房面临着一堆未知的风险 孤身一人承担且不说 找到工作才是痛苦的开始 我已经能想象到 现在的环境 就算拿了offer 进公司也要被压榨被劝退被压力被卡试用期 各种卡 像我这种抗压能力弱的更是完蛋双非更是只能吃💩味巧克力哪有什么小而美 全都是庙小妖风大这个时代已经由不得人了 共勉吧
只写bug的程序媛:
之前一个重庆的,5k还要线下面试,笑死,这些人是从十几年前穿越过来的吗
点赞
评论
收藏
分享
05-12 20:25
华南理工大学 Java
985 27届Java后端一个面试都没有
boss上100多个沟通(大中小都有),只有10个不到要了简历...一个面试也没有。小东西想请大佬们看看,求指点,是不是项目太简单了。目前背完了八股但是leetcode100还没怎么刷,我自己有点想笨鸟先飞,哪怕是个中小厂实习也好。。绝对没有急功近利的意思,听劝, 求亲喷
Code_pancla:
985加大加粗,打招呼改一改吹点牛
点赞
评论
收藏
分享
05-29 17:14
江苏省宝应中学 后端
2025年5月6日 飞猪Java一面
锐评鸡蛋鸭蛋荷包蛋 我的蛋什么时候才能上巅峰凤凰蛋?1. 如何保证数据库数据和redis数据一致性数据库数据和 redis 数据不一致是在 高并发场景下更新数据的情况首先我们要根据当前保持数据一致性的策略来决定方案如果采取的策略是先删除缓存 更新数据库我们假设现在有一个写线程 如果在更新数据库的时候 读线程进入后 读取了脏数据 并且挂载缓存之后写线程更新完毕了数据 但是缓存里还是老数据就会造成数据不一致的问题这种策略的缓存数据库不一致容易发生因为 写入数据库的时间往往大于读取数据的时间所以写线程往往会慢于读线程结束这种策略的优化方案就是采用延迟双删 即是在写线程在更新数据库 前后 都清空缓存 ...
27双非 Java后端开...
面试问题记录
牛客创作赏金赛
点赞
评论
收藏
分享
评论
点赞成功,聊一聊 >
点赞
收藏
分享
评论
提到的真题
返回内容
全站热榜
更多
1
...
选offer还是选爱情?
1858
2
...
六月还有机会的,对吗?
1726
3
...
你的经历比较单薄, 但简历又弥补了这一点--双非仔个人简历分享
1347
4
...
发现27282届的同学怎么越来越卷了,投个票看看相互的进度吧
1060
5
...
记录一下选择
922
6
...
大模型面经(第一期)
898
7
...
怎么包装实习经历呢
863
8
...
27届2天速通美团到店(用户增长)
604
9
...
27双非本 飞轮数据科技 北京小厂(已offer)
564
10
...
25 暑期实习&秋招面经
494
创作者周榜
更多
正在热议
更多
#
写给毕业5年后的自己
#
6190次浏览
118人参与
#
你的秋招第一场笔试是哪家
#
128051次浏览
1393人参与
#
华泰证券Fintech星战营
#
189194次浏览
246人参与
#
职场捅娄子大赛
#
329705次浏览
3331人参与
#
材料专业就业可以去哪些企业岗位
#
32672次浏览
314人参与
#
一人一个landing小技巧
#
63439次浏览
990人参与
#
今年形式下双非本找得到工作吗
#
132113次浏览
1002人参与
#
硬件应届生薪资是否普遍偏低?
#
69972次浏览
506人参与
#
你的论文盲审过了没?
#
102938次浏览
1468人参与
#
机械人的薪资开到多少,才适合去?
#
107505次浏览
445人参与
#
国央企笔面经互助
#
130339次浏览
1083人参与
#
制造业的秋招小结
#
87889次浏览
1605人参与
#
毕业季等于分手季吗
#
21169次浏览
270人参与
#
机械制造秋招总结
#
51004次浏览
494人参与
#
哪些公司笔/面试难度大?
#
2314次浏览
19人参与
#
计算机专业还有必要去大厂卷吗
#
22125次浏览
115人参与
#
好好告别我的学生时代
#
54907次浏览
972人参与
#
毕业后不工作的日子里我在做什么
#
173358次浏览
1524人参与
#
如果再来一次,你还会学硬件吗
#
123000次浏览
1400人参与
#
海信求职进展汇总
#
65882次浏览
363人参与
#
机械制造岗投递时间线
#
22383次浏览
342人参与
牛客网
牛客企业服务