首页 > 系统相关 >面试必会(嵌入式)-C语言面试高频(内存管理)

面试必会(嵌入式)-C语言面试高频(内存管理)

时间:2024-12-12 18:57:18浏览次数:11  
标签:初始化 释放 嵌入式 面试 内存 碎片 C语言 分配 指针

1.(内存)堆和栈的区别

堆栈空间分配不同:

  • 栈由操作系统自动进行分配和释放,用于存放函数的参数值、局部变量的值等,具有高效性。
  • 堆:一般由程序员手动进行分配和释放,效率比栈低很多。
  • data数据区:存放全局变量,静态变量。

堆栈缓存方式不同:

  • 栈使用一级缓存,存储在处理器核心中,调用完成后立即释放,速度较快。
  • 堆存储在二级缓存或主存中,速度相对较慢。

生长方向

  • 堆:堆的分配方向是向上的,即向地址较大的方向分配。当堆需要扩展时,会向高地址方向增长。
  • 栈:栈的分配方向是向下的,即向地址较小的方向分配。当栈需要扩展时,会向低地址方向增长。

生命周期:

  • 堆:堆上的内存在分配时并不会被立即释放,需要手动进行内存释放操作。堆上的数据可以在程序的任意位置进行访问,不受函数的调用关系限制。
  • 栈:栈上的内存分配和释放是自动进行的,随着函数的调用和返回进行相应的操作。栈上的数据只在特定的作用域内有效,函数执行完成后会自动释放。

空间大小:

  • 栈的空间大小一般较小,通常最多为2MB,超过则会报溢出错误。
  • 堆的空间比较大,理论上可以接近3GB(对于32位程序来说)。

能否产生碎片:

  • 栈操作遵循"后进先出"的原则,不会有内存块从栈中弹出,因此不会产生碎片。
  • 堆是通过动态分配内存的方式进行分配和释放,频繁的申请和释放内存可能会引发内存碎片问题。

2.在函数中申请堆内存需要注意什么

  • 确保不要错误地返回指向栈内存的指针,因为栈内存会在函数结束时自动释放。
  • 避免在函数内部申请临时数组,因为函数执行完成后,该数组会消失。
  • 不要返回指向常量区的内存空间,因为它们无法修改且获取它们没有意义。
  • 使用传入一级指针无法解决问题,因为函数内部指针的修改不会影响传入的指针。
  • 在分配堆内存时,确保空间足够存储所需数据,避免访问越界和产生未定义行为。

解决办法如下:

  • 使用二级指针来返回申请的堆内存的地址,通过间接引用来修改指针值,从而确保在函数外部能够获取到堆内存的内容。
  • 使用指针函数来解决问题,即返回通过malloc函数申请的堆内存的地址,这样可以在函数外部使用free函数释放该内存。

3.请你说说内存碎片⭐

内存碎片是指在内存管理过程中产生的未被有效利用的零散、不连续的内存空间。主要分为两种类型:内部碎片外部碎片

  • 内部碎片:是由于固定大小的内存分配方式或对齐要求等原因导致的未被利用的小空间。当分配给进程的内存块大于所需的大小时,其中的剩余空间就成为了内部碎片。
  • 外部碎片:是由于存在未分配的连续内存空间太小而不能满足分配请求,从而导致这些内存无法被有效利用。

解决内存碎片问题的方法可以包括:

  • 段页式管理:采用虚拟内存管理技术,将物理内存划分为不同的页或段,以更灵活地管理和分配内存空间,减少碎片化。
  • 使用内存池:通过分配一定数量的内存块,由内存池来管理分配和回收,减少频繁的内存分配和释放,从而减少碎片化。

4.什么是内存池⭐

内存池(Memory Pool)是一种动态内存分配与管理技术。通常情况下习惯使用new/delete/malloc/free等API申请分配和释放内存,这样导致的后果是:当程序长时间运行时,由于所申请的内存块大小不定,频繁使用时会造成大量的内存碎片从而降低程序和操作系统的性能。内存池则是在真正使用内存之前,先申请分配一大块内存(内存池)留作备用,当我们申请内存时,从池中取出一块动态分配的内存,释放内存时,再将我们使用的内存释放到我们申请的内存池内,再次申请内存池也可以再取出来使用。并且,尽量与周边的内存块合并。若内存池不够时,则自动扩大内存池,从操作系统中申请更大的内存池

为什么需要内存池?

解决内存碎片问题

假设系统依次分配了16,16,4,8,8字节,接着释放了16字节和8字节归还给了系统(红色表示还未归还给系统),那么现在堆区空闲了一个16字节的空间和一个8字节的空间。如果用户要想申请出一个24字节的空间,那么系统就无法分配连续的空间给用户。这就是内存碎片的问题。

5.使用指针需要注意什么?⭐⭐

  1. 定义指针时,最好将其初始化为 NULL,这样可以避免使用未初始化的指针。
  2. 在使用 malloc 或 new 分配内存之后,应该立即检查指针的值是否为 NULL,以防止在使用指针值为 NULL 的内存时出现问题。在 C++11 及更高版本中,使用 new 分配内存的时候,如果发生错误,会抛出异常,因此不需要显示判空。
  3. 当使用指针指向数组或者动态分配的内存时,一定要记得为其赋初值,避免使用未被初始化的内存作为右值。
  4. 小心避免数字或指针的下标越界,特别是一些常见的“多1”或者“少1”错误。
  5. 在释放动态分配的内存时,务必确保申请和释放是配对的,以防止内存泄漏。
  6. 使用 free 或 delete 释放内存后,立即将指针设置为 NULL,以避免出现悬空指针(野指针)。
  7. 避免指针的多重间接性:多重间接性指的是通过多个指针进行连续的解引用操作。尽量避免过多的间接性,以提高代码的可读性和维护性。
  8. 注意浅拷贝和深拷贝:如果指针指向的是动态分配的内存,涉及到对象的拷贝或赋值时,需要注意浅拷贝和深拷贝的区别。深拷贝会复制整个对象及其指向的内存,而浅拷贝只是复制指针本身,可能导致多个指针指向同一块内存。

6.初始化为0的全局变量在bss还是data

初始化为0的全局变量通常会被分配到程序的BSS(Block Started by Symbol)段。BSS段是用于存放未初始化或初始化为0的全局变量和静态变量的一部分内存空间。在程序加载时,系统会自动将BSS段中的变量初始化为0。

已经明确初始化为非零值的全局变量会被分配到程序的数据(Data)段。Data段用于存放已经初始化的全局变量和静态变量。

总结来说,初始化为0的全局变量通常会被分配到BSS段,而已初始化为非零值的全局变量则会被分配到Data段。

标签:初始化,释放,嵌入式,面试,内存,碎片,C语言,分配,指针
From: https://blog.csdn.net/m0_67794241/article/details/144411345

相关文章

  • 中高级运维工程师运维面试题(二)之NGINX
    这里写目录标题前言基础知识1.什么是NGINX?2.NGINX的基本架构是怎样的?3.如何配置NGINX以支持HTTPS?高级配置4.如何配置NGINX的负载均衡?5.如何实现URL重写和重定向?6.如何配置NGINX的缓存?性能优化7.如何优化NGINX的并发性能?安全性8.如何保护NGINX......
  • C语言(内存管理)
    main函数原型定义:main函数有多种定义格式,main函数也是函数,函数相关的结论对main函数也有效(也可以定义main函数的函数指针)。main函数的完整写法:intmain(intargc,char*argv[]){}intmain(intargc,char**argv){}扩展写法:main(){}等价intmain(){}intmain......
  • 蓝桥杯嵌入式模板创建(STM32 CubeMx简单使用教程)
    蓝桥杯嵌入式新板模板创建&简单经验分享补充在最前:以下原文是22年还未毕业时写的,仅在把板子二手卖给别人的时候给别人分享了这份笔记。那时经验不多,现在也由于工作使用的芯片不同已很久没有使用CubeMX了,因此文章可能有很多错漏之处,欢迎在评论区指出。备注在前:uint8_t即un......
  • 链表的一步步实现(需有一部分c语言基础)【缓慢更新中
    链表的一步步实现(需有一部分c语言基础)(由于本人上课实在没学懂链表的具体实现步骤,于是写下这篇博客记录学习过程,有兴趣的新手也可以跟着学习1.认识链表的结构&创建简单静态链表并输出数据Q:什么是链表?A:链表是由一系列节点组成,每个节点包含两个域,一个是数据域,用来保存数据,另外一......
  • C语言-排序
    常见的排序算法分为以下四种,插入排序,选择排序,交换排序,归并排序。一、插入排序(一)直接插入排序直接插入排序,将一段数组看做被分成已排序序列和未排序序列,排序过程是从未排序序列的元素开始,该元素与已排序序列的元素从后向前扫描,找到第一个小于(或大于)该元素的已排序项,然后将......
  • C语言:指针(2)
    字符数组和字符指针字符串的实现在C语言中,表示一个字符串有以下两种形式:用字符数组存放一个字符串,用字符指针指向一个字符串案例:/***字符串的两种实现方式*/ //方式1:使用字符数组实现字符串 charstr[]="ILOVRYOU"; printf("%s\n",str); //使用字符指针实现字......
  • C语言基础:数组(一维数组)
    引例如果我们要在程序中表示一个学生的成绩,我们会使用一个int来表示,如:intscore。假如我们要在程序中表示一组成绩,此时我们所学的常规的数据类型就无法再表示,这个时候我们就需要使用到一种新的表现形式,这种表现形式就是我们的数组。什么是数组数组是相同类型,有序数据的集合......
  • C语言基础:数组(二维数组)
    数组二维数组定义:二维数组本质上是一个行列式的组合,也就是说二维数组是由行和列两部分组成。二维数组数据是通过行列解读。二维数组可被视为一个特殊的一维数组,相当于二维数组又是一个一维数组,只不过它的元素是一维数组。(也就是说数组的元素可以是数组类型)。语法 类型......
  • 韶音科技嵌入式面试题及参考答案
    Bootloader的启动流程是什么?Bootloader是在操作系统内核运行之前运行的一段小程序。它的启动流程主要分为以下几个阶段。首先是硬件初始化阶段。这个阶段会对处理器以及一些关键的硬件设备进行初始化。比如,会配置处理器的工作模式、设置堆栈指针等。以ARM处理器为例,会......
  • 大数据开发面试题整理
    ​一、数据仓库相关1.什么是数据仓库?请简述其特点和用途。答案:数据仓库是一个面向主题的、集成的、相对稳定的、反映历史变化的数据集合,用于支持管理决策。特点包括:面向主题:围绕特定的主题组织数据,如销售、客户、产品等。集成性:整合来自多个数据源的数据,消除数据......