第 3 章:嵌入式 C 语言与数据结构面试
本章聚焦面试中最高频的 C 语言与数据结构考点:指针本质、内存管理、链表操作及算法实现。大厂面试不仅考察代码正确性,更关注底层原理理解、边界条件分析和安全编程意识。下方框架图可帮助你在回答时建立结构化表达路径。
面试问答卡片
面试问题:指针和数组在函数参数传递时有何区别?
面试官考察点:理解数组名退化为指针、sizeof 行为差异、函数参数传递的本质。
回答框架:
- 明确指出数组名是首元素地址常量,但作为函数实参时退化为指针;
- sizeof(数组) 在函数内外不同,形参中 sizeof(指针) 恒为4/8;
- 给出代码示例并对比输出;
- 强调使用数组时需传递长度信息。
示例回答(60~80字):
“数组名本质为首元素地址常量,不可修改。作为实参传递给函数时,数组名退化为指针,因此函数内 sizeof(arr) 得到的是指针大小而非数组容量。例如 void foo(int arr[]) 等价于 void foo(int *arr),应额外传入数组长度。”
可能追问:
- “如何让函数内 sizeof 正确返回数组大小?”(答:使用数组引用或宏计算)
- “二维数组传参为什么需要指定列数?”
面试问题:什么是内存泄漏?在嵌入式系统中如何防止?
面试官考察点:动态内存管理机制、堆栈区别、嵌入式资源受限环境下的安全编程。
回答框架:
- 定义:动态分配的内存未释放导致内存耗尽;
- 嵌入式特点:堆空间有限,频繁 malloc/free 会产生碎片;
- 给出防御手段:配对检查、内存池、静态分配优先;
- 辅以代码示例(如释放后置 NULL)。
示例回答(80~100字):
“内存泄漏指 malloc 分配的内存未调用 free 释放,导致系统可用内存逐渐减少。在嵌入式环境中,堆空间通常只有几 KB,我优先使用静态数组或内存池分配。若确需动态分配,我会确保每一个 malloc 都有对应的 free,并在释放后立即将指针置 NULL,同时使用宏进行配对计数监控。”
可能追问:
- “如何用工具检测内存泄漏?”(答:静态分析工具如 PC-Lint,动态记录 malloc/free 调用栈)
- “RTOS 下堆管理有何不同?”(答:注意任务栈和系统堆区分)
面试问题:请反转单链表,并分析时间复杂度。
面试官考察点:链表基本操作、指针修改顺序、迭代与递归差异、边界情况处理。
回答框架:
- 先定义节点结构;
- 迭代法:三个指针 prev/cur/next 逐个反转;
- 递归法:假设已反转后续,处理当前节点;
- 分析 O(n) 时间、O(1) 或 O(n) 空间;
- 注意空链表和单节点边界。
示例回答(80~100字):
“反转单链表通常用迭代法:初始化 prev=NULL,cur=head,while 循环中保存 next = cur->next,令 cur->next = prev,然后 prev=cur, cur=next。最终返回 prev 作为新头。时间复杂度 O(n),空间 O(1)。若用递归,代码更简洁但栈空间 O(n),嵌入式下不推荐。需同时处理输入为 NULL 或只有一个节点的边界。”
可能追问:
- “递归反转的深度限制是多少?”(答:链表长度可能超过默认栈空间,需谨慎)
- “如果要求常数空间且不修改原始链表,如何实现?”
项目案例模板
| 项目名称 | 基于 STM32F4 的 IAP Bootloader 开发 |
|---|---|
| 业务背景 | 产品需支持远程固件升级(OTA),通过串口接收升级包并写入内部 Flash,实现双区备份回滚。 |
| 规模指标 | Bootloader 代码量 ~1200 行;支持 3 种芯片型号;Flash 总容量 1MB,双区各 512KB。 |
| 技术栈 | C(裸机)、STM32 HAL、内部 Flash 驱动、CRC32 校验、Ymodem 协议 |
| 核心难点 |
|
| 面试高频追问 |
|
| 可迁移回答话术 | “在 IAP 项目中我深入理解了 Flash 物理特性与地址映射,这让我能快速定位内存对齐、指针跳转类问题。无论面试官问的是指针函数、内存管理还是链表,我都能联系到底层地址操作的实际案例。” |