嵌入式面试高频!!!C语言(四)(嵌入式八股文,嵌入式面经)
更多嵌入式面试文章见下面连接,会不断更新哦!!关注一下谢谢!!!!
https://blog.csdn.net/qq_61574541/category_12976911.html?fromshare=blogcolumn&sharetype=blogcolumn&sharerId=12976911&sharerefer=PC&sharesource=qq_61574541&sharefrom=from_link
https://blog.csdn.net/qq_61574541/category_12976911.html?fromshare=blogcolumn&sharetype=blogcolumn&sharerId=12976911&sharerefer=PC&sharesource=qq_61574541&sharefrom=from_link
一、volatile
关键字和 extern
关键字
1. 作用
- 禁止编译器优化:告诉编译器 “这个变量的值可能随时以不可预知的方式被改变”,因此每次访问都必须从内存中读取,而非使用寄存器中的缓存值。
- 典型场景:
- 硬件寄存器:如嵌入式系统中的 I/O 端口。
- 多线程共享变量:被中断服务程序(ISR)修改的变量。
- 内存映射设备:如 LCD 控制器的内存地址。
volatile int status_reg; // 硬件状态寄存器// 编译器不会优化对status_reg的读取
while (status_reg != 0); // 每次循环都从内存读取status_reg
2. 生效阶段
- 编译阶段:编译器在生成机器码时,会强制每次都从内存读取 / 写入
volatile
变量,而非依赖寄存器缓存。
二、extern
关键字
1. 作用
- 声明外部链接:告诉编译器 “这个变量或函数的定义在其他文件中”,从而避免重复定义。
- 典型场景:
- 多文件项目:在头文件中声明全局变量或函数,在源文件中定义。
- 跨语言调用:如 C++ 调用 C 函数时使用
extern "C"
。
// 文件1.c
int global_var = 10; // 定义全局变量// 文件2.c
extern int global_var; // 声明外部变量
printf("%d", global_var); // 使用全局变量
3. 生效阶段
- 链接阶段:编译器生成目标文件时,
extern
声明的符号会被标记为 “外部引用”,由链接器在其他目标文件中查找对应的定义。
三、核心区别对比
特性 | volatile | extern |
---|---|---|
作用 | 禁止编译器优化,强制内存访问 | 声明外部定义的变量或函数 |
生效阶段 | 编译阶段 | 链接阶段 |
影响范围 | 单个变量或对象 | 整个项目中的符号解析 |
典型场景 | 硬件交互、多线程共享变量 | 多文件项目、跨语言调用 |
与内存的关系 | 每次访问都从内存读取 / 写入 | 不涉及内存分配,仅声明符号存在 |
四、常见误区
1. volatile
用于多线程同步
- 错误:认为
volatile
能替代同步机制(如互斥锁)。 - 真相:
volatile
仅阻止编译器优化,但无法解决多线程竞争问题(如原子性)。
2. extern
用于定义变量
- 错误:在头文件中写
extern int x = 10;
。 - 真相:
extern
仅声明变量,定义必须在源文件中进行(int x = 10;
)。
二、堆和栈有什么不同
一、基本概念
1. 栈(Stack)
- 内存分配:由操作系统自动管理,遵循 后进先出(LIFO) 原则。
- 存储内容:函数调用的上下文(局部变量、参数、返回地址等)。
- 典型场景:函数内部的局部变量、函数调用过程中的参数传递。
2. 堆(Heap)
- 内存分配:由程序员手动管理(如 C 语言的
malloc
/free
,C++ 的new
/delete
)。 - 存储内容:动态分配的数据(如对象、数组等)。
- 典型场景:需要在函数外部长期存在的数据(如动态数组、链表)。
二、核心区别对比
特性 | 栈(Stack) | 堆(Heap) |
---|---|---|
内存分配方式 | 自动分配和释放(由系统管理) | 手动分配和释放(需程序员干预) |
分配效率 | 高(直接移动栈指针) | 低(需动态查找可用内存块) |
内存空间大小 | 通常较小(如几 MB 到几十 MB) | 通常较大(受限于物理内存和虚拟内存) |
数据生命周期 | 随函数调用结束而销毁 | 直到显式释放(如调用 free ) |
内存碎片问题 | 不存在碎片 | 可能产生碎片(频繁分配 / 释放导致) |
生长方向 | 向低地址扩展(栈顶向下移动) | 向高地址扩展(堆顶向上移动) |
内存连续性 | 连续 | 不连续(碎片化) |
使用场景 | 局部变量、函数调用上下文 | 动态数据结构(如链表、树) |
内存访问方式 | 直接访问(通过栈指针) | 间接访问(通过指针) |
三、典型示例
1. 栈内存示例
void func() {int a = 10; // 局部变量,分配在栈上char buffer[20]; // 数组,分配在栈上// 函数结束时,a和buffer自动释放
}
2. 堆内存示例
void dynamic_allocation() {int* p = (int*)malloc(sizeof(int)); // 在堆上分配内存if (p != NULL) {*p = 20; // 通过指针访问堆内存free(p); // 手动释放堆内存}
}
四、常见问题
1. 栈溢出(Stack Overflow)
- 原因:递归过深或局部变量过大,导致栈空间耗尽。
- 解决:减少递归深度、使用堆内存替代大数组。
2. 内存泄漏(Memory Leak)
- 原因:堆内存分配后未释放,导致内存持续占用。
- 解决:确保每次
malloc
/new
后都有对应的free
/delete
。
3. 性能对比
- 栈:分配速度快(约为堆的 10 倍),适合短期使用的数据。
- 堆:分配速度慢,但灵活性高,适合长期存在的数据。
五、总结
场景 | 推荐使用栈 | 推荐使用堆 |
---|---|---|
数据大小 | 小且固定 | 大或动态变化 |
生命周期 | 短期(函数内) | 长期(跨函数) |
性能要求 | 高 | 低 |
内存管理复杂度 | 低(自动管理) | 高(手动管理) |
三、堆栈溢出一般是由什么原因导致的?
一、核心原因:栈空间耗尽
栈内存由操作系统自动管理,存储函数调用帧(局部变量、参数、返回地址等)。当栈空间被占满时,会触发堆栈溢出。常见诱因包括:
二、常见场景与示例
1. 无限递归(最常见)
- 原因:递归函数未正确设置终止条件,导致无限调用。
- 示例:
void recursive() {recursive(); // 无终止条件,无限递归
}int main() {recursive(); // 触发堆栈溢出
}
- 分析:每次递归调用都会在栈上创建新的函数帧,最终耗尽栈空间。
2. 过深的递归调用
- 原因:递归深度过大(如树的遍历深度超过栈容量)。
- 示例:
void deepRecursion(int n) {if (n == 0) return;int arr[1000]; // 每次递归占用大量栈空间deepRecursion(n - 1);
}int main() {deepRecursion(100000); // 可能导致栈溢出
}
3. 大型局部变量
- 原因:在函数内部定义过大的数组或结构体,超出栈空间限制。
- 示例:
void largeArray() {char buffer[1024 * 1024]; // 1MB数组,可能超出栈容量
}
4. 嵌套过深的函数调用
- 原因:非递归的多层函数调用(如 A→B→C→...),每层调用都占用栈空间。
- 示例:
void func1() { func2(); }
void func2() { func3(); }
// ... 更多嵌套函数
void func1000() { /* 占用栈空间 */ }int main() {func1(); // 可能导致深层嵌套
}
5. 栈空间不足(系统限制)
- 原因:操作系统默认栈空间较小(如 Linux 默认 8MB),无法满足程序需求。
- 解决:通过命令行增大栈空间限制(如 Linux 的
ulimit -s
)。三、不同语言的堆栈溢出表现
语言 错误提示 常见诱因 C/C++ Segmentation fault 无限递归、大型局部数组 Java StackOverflowError 无限递归(如 toString () 循环引用) Python RecursionError 递归深度超过限制(默认约 1000 次) JavaScript RangeError: Maximum call stack size exceeded 浏览器环境中的无限递归
四、预防与解决方法
-
修复递归终止条件:确保递归函数有明确的退出条件。
void safeRecursion(int n) {if (n <= 0) return; // 正确终止条件safeRecursion(n - 1);
}
2.使用迭代替代递归:对于深度不确定的场景,用循环代替递归。
void iterative() {while (true) { // 循环实现,不占用栈空间// ...}
}
3.减小局部变量大小:将大型数组或结构体改为动态分配(堆内存)。
void dynamicAllocation() {char* buffer = (char*)malloc(1024 * 1024); // 堆分配if (buffer) {// 使用bufferfree(buffer);}
}
4.增加系统栈限制:在 Linux 中通过 ulimit -s unlimited
临时增大栈空间。
5.尾递归优化:部分语言(如 Python 不支持,C++ 支持)可将递归转换为循环。
// 尾递归示例(需编译器优化)
int tailRecursive(int n, int acc) {if (n == 0) return acc;return tailRecursive(n - 1, acc + n); // 尾递归调用
}
四、内存泄漏和内存池
一、内存泄漏(Memory Leak)
1. 定义
- 程序在堆上动态分配的内存(如
malloc
/new
),在不再使用时未被释放(如未调用free
/delete
),导致这部分内存无法被操作系统回收。
2. 常见原因
- 忘记释放内存:
void leakExample() {int* ptr = (int*)malloc(sizeof(int));// 使用ptr,但未调用free(ptr)
} // 内存泄漏!
- 异常导致的泄漏:函数中途抛出异常,未执行释放代码。
- 循环分配内存:在循环中持续分配内存而不释放。
- 指针丢失:指向内存的指针被覆盖,导致无法释放。
3. 危害
- 长期运行的程序(如服务器)会逐渐耗尽可用内存,最终导致系统崩溃。
- 碎片化内存:频繁分配和释放不同大小的内存块,导致内存碎片化,降低分配效率。
4. 检测工具
- Valgrind(Linux):检测内存泄漏和越界访问。
- AddressSanitizer(ASan):GCC/Clang 内置的快速内存错误检测工具。
- Visual Studio 内存分析器:Windows 平台下的内存调试工具。
二、内存池(Memory Pool)
1. 定义
- 一种内存管理技术,预先分配大块内存(池),然后按需分配小块内存给程序使用。当程序释放内存时,内存块不直接返回给操作系统,而是返回给池以便后续复用。
2. 核心思想
- 预分配:一次性分配大块内存,减少系统调用次数。
- 复用:回收的内存块不释放,直接复用,避免频繁分配 / 释放。
- 减少碎片:通过固定大小的内存块或智能分配算法,减少内存碎片。
3. 简单实现示例
#include <stdlib.h>// 内存池节点结构
typedef struct MemNode {struct MemNode* next;char data[1]; // 实际数据从这里开始
} MemNode;// 内存池结构
typedef struct {MemNode* freeList; // 空闲链表size_t blockSize; // 每个内存块大小size_t chunkSize; // 每次分配的块数量
} MemPool;// 初始化内存池
MemPool* createPool(size_t blockSize, size_t chunkSize) {MemPool* pool = (MemPool*)malloc(sizeof(MemPool));pool->blockSize = blockSize;pool->chunkSize = chunkSize;pool->freeList = NULL;return pool;
}// 从内存池分配内存
void* poolAlloc(MemPool* pool) {if (pool->freeList == NULL) {// 无空闲块,分配新的一组块size_t realSize = sizeof(MemNode) + pool->blockSize - 1;MemNode* chunk = (MemNode*)malloc(realSize * pool->chunkSize);// 将新分配的块加入空闲链表for (size_t i = 0; i < pool->chunkSize; i++) {MemNode* node = &chunk[i];node->next = pool->freeList;pool->freeList = node;}}// 从空闲链表取出一个块MemNode* node = pool->freeList;pool->freeList = node->next;return &node->data;
}// 释放内存回池
void poolFree(MemPool* pool, void* ptr) {MemNode* node = (MemNode*)((char*)ptr - offsetof(MemNode, data));node->next = pool->freeList;pool->freeList = node;
}// 销毁内存池
void destroyPool(MemPool* pool) {// 实际实现中需遍历所有分配的块并释放free(pool);
}
4. 优势
- 高性能:减少系统调用(
malloc
/free
),分配速度提升 5-10 倍。 - 减少碎片:通过固定大小的内存块或分桶策略,降低内存碎片。
- 控制内存:可预测的内存使用模式,避免内存泄漏。
5. 适用场景
- 频繁分配 / 释放小对象:如网络服务器中的连接请求处理。
- 实时系统:需要确定性的内存分配时间(如游戏引擎)。
- 内存碎片敏感场景:长期运行的程序(如数据库、中间件)。
三、内存泄漏 vs 内存池
特性 | 内存泄漏 | 内存池 |
---|---|---|
本质 | 内存管理错误 | 内存管理优化技术 |
原因 | 未释放不再使用的内存 | 预分配和复用内存块 |
危害 | 内存耗尽、系统崩溃 | 可能占用更多常驻内存 |
解决方法 | 检测工具(Valgrind)、RAII | 自行实现或使用第三方库(如 Boost) |
性能影响 | 无直接影响(但可能导致系统变慢) | 显著提升分配速度 |
适用场景 | 所有动态内存分配场景 | 高频分配 / 释放相同大小内存的场景 |
四、高级内存池技术
- 分级内存池:按不同大小分桶管理内存块。
- 线程私有内存池:每个线程独立维护内存池,避免锁竞争。
- 内存池与垃圾回收结合:在某些语言(如 Python)中自动回收不再使用的内存池。
五、指针的运算
int *ptr//假设地址为0x00 00 00 00 (32位系统)
prt++;//0x00 00 00 04
对于32位系统所有类型的指针都只占4字节的空间。
相关文章:

嵌入式面试高频!!!C语言(四)(嵌入式八股文,嵌入式面经)
更多嵌入式面试文章见下面连接,会不断更新哦!!关注一下谢谢!!!! https://blog.csdn.net/qq_61574541/category_12976911.html?fromshareblogcolumn&sharetypeblogcolumn&…...
数据治理在制造业的实践案例
一、数据治理在制造业的重要性 随着工业4.0的到来,制造业正经历着前所未有的变革。数据治理作为制造业数字化转型的关键组成部分,对提升企业竞争力、优化生产流程、提高产品质量和客户满意度等方面起着至关重要的作用。在制造业中,数据治理不仅涉及到数据的收集、存…...
【强化学习】——03 Model-Free RL之基于价值的强化学习
【强化学习】——03 Model-Free RL之基于价值的强化学习 \quad\quad \quad\quad 动态规划算法是基于模型的算法,要求已知状态转移概率和奖励函数。但很多实际问题中环境 可能是未知的,这就需要不基于模型(Model-Free)的RL方法。 \quad\quad 其又分为: 基于价值(Valu…...

Edge(Bing)自动领积分脚本部署——基于python和Selenium(附源码)
微软的 Microsoft Rewards 计划可以通过 Bing 搜索赚取积分,积分可以兑换礼品卡、游戏等。每天的搜索任务不多,我们可以用脚本自动完成,提高效率,解放双手。 本文将手把手教你如何部署一个自动刷积分脚本,并解释其背…...
html表格转换为markdown
文章目录 工具功能亮点1.核心实现解析1. 剪贴板交互2. HTML检测与提取3. 转换规则设计 2. 完整代码 在日常工作中,我们经常遇到需要将网页表格快速转换为Markdown格式的场景。无论是文档编写、知识整理还是数据迁移,手动转换既耗时又容易出错。本文将介绍…...

VsCode 安装 Cline 插件并使用免费模型(例如 DeepSeek)
当前时间为 25/6/3,Cline 版本为 3.17.8 点击侧边栏的“扩展”图标 在搜索框中输入“Cline” 找到 Cline 插件,然后点击“安装” 安装完成后,Cline 图标会出现在 VS Code 的侧边栏中 点击 Use your own API key API Provider 选择 OpenRouter…...

短视频矩阵系统源码新发布技术方案有那几种?
短视频矩阵运营在平台政策频繁更迭的浪潮中,已成为内容分发的核心战场。行业领先者如筷子科技、云罗抖去推、超级编导等平台,其稳定高效的代发能力背后,离不开前沿技术方案的强力支撑。本文将深入剖析当前主流的六大短视频矩阵系统代发解决方…...

React 第五十二节 Router中 useResolvedPath使用详解和注意事项示例
前言 useResolvedPath 是 React Router v6 提供的一个实用钩子,用于解析给定路径为完整路径对象。 它根据当前路由上下文解析相对路径,生成包含 pathname、search 和 hash 的完整路径对象。 一、useResolvedPath 核心用途 路径解析:将相对…...
【PmHub面试篇】性能监控与分布式追踪利器Skywalking面试专题分析
你好,欢迎来到本次关于PmHub整合性能监控与分布式追踪利器Skywalking的面试系列分享。在这篇文章中,我们将深入探讨这一技术领域的相关面试题预测。若想对相关内容有更透彻的理解,强烈推荐参考之前发布的博文:【PmHub后端篇】Skyw…...

Cursor快速梳理ipynb文件Prompt
1. 整体鸟瞰 请在不运行代码的前提下,总结 <文件名.ipynb> 的主要目的、核心逻辑流程和输出结果。阅读整个项目目录,列出每个 .ipynb / .py 文件的角色,以及它们之间的数据依赖关系(输入→处理→输出)。2. 结构…...

天机学堂-分页查询
需求 分页查询我的课表 返回: 总条数、总页数、当前页的课表信息的集合 返回的VO(已经封装成统一的LearningLessonsVO) 定义Controller RestController RequestMapping("/lessons") RequiredArgsConstructor public class Lear…...
业态即战场:零售平台的生意模型与系统设计解构
目录 一、当我们在电商买菜、点外卖时,其实是零售业态在进化 (一)从“商场选货”到“算法推货”:零售的时代已经不同 (二)“控货”和“卖场”——零售的两种基本商业模式 二、四种经典零售业态解析:控货 vs 卖场,地面 vs 线上 (一)地面控货零售:直营模式的黄金…...

微算法科技(NASDAQ:MLGO)基于信任的集成共识和灰狼优化(GWO)算法,搭建高信任水平的区块链网络
随着数字化转型的加速,区块链技术作为去中心化、透明且不可篡改的数据存储与交换平台,正逐步渗透到金融、供应链管理、物联网等多个领域,探索基于信任的集成共识机制,并结合先进的优化算法来提升区块链网络的信任水平,…...

全新Xsens Animate版本是迄今为止最大的软件升级,提供更清晰的数据、快捷的工作流程以及从录制开始就更直观的体验
我们整合了专业人士喜爱的 Xsens 动捕功能,并使其更加完善。全新Xsens Animate版本是我们迄今为止最大的软件升级,旨在提供更清晰的数据、更快捷的工作流程以及从录制开始就更直观的体验。 从制作游戏动画到流媒体直播头像或构建实时电影内容࿰…...

大语言模型评测体系全解析(下篇):工具链、学术前沿与实战策略
文章目录 一、评测工具链:从手工测试到自动化工程的效率革命(一)OpenCompass:开源评测框架的生态构建1. 技术架构:三层架构实现评测自动化2. 开发者赋能:从入门到进阶的工具矩阵 (二)…...

python打卡day46@浙大疏锦行
知识点回顾: 不同CNN层的特征图:不同通道的特征图什么是注意力:注意力家族,类似于动物园,都是不同的模块,好不好试了才知道。通道注意力:模型的定义和插入的位置通道注意力后的特征图和热力图 内…...
C++.OpenGL (1/64) 创建窗口(Hello Window)
OpenGL 创建窗口(Hello Window) 步骤详解与代码实现 #mermaid-svg-436DlGvysFQogISc {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-436DlGvysFQogISc .error-icon{fill:#552222;}#mermaid-svg-436DlGvysFQogISc…...

Excel 发现此工作表中有一处或多处公式引用错误。请检查公式中的单元格引用、区域名称、已定义名称以及到其他工作簿的链接是否均正确无误。弹窗
Excel 提示“发现此工作表中有一处或多处公式引用错误”通常表示公式中存在无效引用。以下是系统化的检查步骤,帮助你定位和修复问题: 1. 检查单元格引用: 无效单元格引用:检查公式中的单元格地址(如 A1、B10&…...

NVIDIA DRIVE AGX平台:引领智能驾驶安全新时代
随着科技的不断进步,汽车行业正迎来前所未有的变革,智能驾驶技术成为全球产业竞相布局的焦点之一。然而,这场技术革命的背后,最关键且被广泛关注的是安全性问题。近日,我认真研读了NVIDIA发布的《自动驾驶安全报告》白…...

推荐12个wordpress企业网站模板
WordPress企业网站模板是一种专为企业网站设计的WordPress主题,旨在帮助企业创建专业、美观且易于管理的网站。这些模板通常具备响应式设计、SEO优化、多语言支持等功能,能够满足不同行业和企业的需求。 WordPress企业网站模板的适用场景 企业官网&…...

沙市区举办资本市场赋能培训会 点赋科技分享智能消费新实践
荆州市沙市区,2025年6月5日—— 在沙市区政府主办的“发挥区域性股权市场功能,助力企业拥抱资本市场”专题培训会上,区委副书记、区长郭熙胜强调要充分发挥资本市场服务实体经济功能,推动本土创新企业高质量发展。区内重点企业点赋…...
Docker 容器化基础:镜像、容器与仓库的本质解析
Docker 概念与容器化技术 Docker 是一种容器化平台,能够将应用程序及其依赖项打包成一个容器,确保在任何环境中都能一致运行。容器化技术通过操作系统级别的虚拟化,为应用程序提供了一个独立的运行环境。 容器化技术的核心优势 一致性&…...
九.C++ 对引用的学习
一.基本概念 引用即内存的别名 int a 10; int& b a; 引用本身不占用内存,并非实体,对引用的所有操作都是在对目标内存进行操作 引用必须初始化,且不能更换对象 int c 5; b c; // 仅仅是在对引用的目标内存进行赋值 #include <ios…...

探秘鸿蒙 HarmonyOS NEXT:实战用 CodeGenie 构建鸿蒙应用页面
在开发鸿蒙应用时,你是否也曾为一个页面的布局反复调整?是否还在为查 API、写模板代码而浪费大量时间?今天带大家实战体验一下鸿蒙官方的 AI 编程助手——CodeGenie(代码精灵) ,如何从 0 到 1 快速构建一个…...

art-pi2 上手记录(二)
功能比较庞杂,写得不好,抛砖引玉 预备知识 stm32 默认从主闪存0x08000000启动 art-pi2的psram 映射0x90000000 art-pi2的8线ospi flash 映射0x70000000 stm32h7比较灵活,通过修改选项字节,可以实现从 0x0000 0000 到 0x3FFF 0…...

数据库SQLite基础
SQLite的存储结构 --->B树 大型数据库 :Oracle 中型数据库 :Server是微软开发的数据库产品,主要支持windows平台 小型数据库 : MySQL是一个小型关系型数据库管理系统。开放源码 (嵌入式不需要存储太多数据) 一、SQLite基础 SQLite的源代码…...

1.3 古典概型和几何概型
文章目录 古典概型模型(等可能模型)几何概型 古典概型模型(等可能模型) 两个条件: 1) 有限个样本点 2) 等可能性 例题: 设有n个人,每个人都等可能地被分配到N个房间中的任一间(n≤N), 求下列事件的概率: (1)某指定的n间房…...

html-pre标签
我们都知道在常见标签里面的文字的格式是不会显示的,比如你打了多个空格,但却不会显示,而pre标签会显示。 主要特点: 保留空格和换行:在 <pre> 标签内,HTML 会保留所有的空格、换行符和制表符等格式…...

【WPF】WPF 项目实战:用ObservableCollection构建一个可增删、排序的管理界面(含源码)
💡WPF 项目实战:构建一个可增删、排序的光源类型管理界面(含源码) 在实际的图像处理项目中,我们经常需要对“光源类型”进行筛选或管理。今天我们来一步步构建一个实用的 WPF 界面,实现以下功能࿱…...

MCU_IO驱动LED
注意事项: 1、亮度要求较高的情况下,不能由IO直接驱动LED MCU_IO引脚输出的电压和电流较弱,如果对光的亮度有要求的话,需要使用三极管来驱动。 MCU_IO的电压一般为3.3V或者5V,输出电流一般10mA-25mA。 2、不同颜色…...