华大嵌入式软件开发一面:MCU/RTOS 项目与 C 语言细节
- 轮次
- 一面
- 时间
- 2026-09
- 来源
- 牛客网
《面试题目》
- 用一两分钟介绍下自己,以及和嵌入式岗位最相关的经历。
- 简历上最熟的嵌入式项目,背景、架构和你负责的模块分别是什么?
- 你的项目更偏 MCU/RTOS 还是 Linux 应用?核心模块怎么切的?
- volatile 在嵌入式里什么场景必须用?不用会出什么问题?
- 指针数组和数组指针有什么区别?举个你会在工程里用到的例子。
《参考解析》
项目怎么讲:按「背景—架构—我负责什么—交付指标」四段说。背景讲清楚是工业中控,做采集、本地控制和上位交互;架构讲双核分工(CM7 跑主业务与协议栈,CM4 分担实时采集或辅助通信);自己的模块讲任务调度、UART/TCP 链路、应用层组帧校验、双核共享内存交互,以及 Flash 分区与升级跳转;最后落到指标——通信稳定性、控制时序、异常可复现定位。模块划分回答「驱动层 / 协议层 / 业务层 / 升级与双核同步」这种分层表述比逐个函数罗列更有说服力。
为什么这么分层:驱动层管外设初始化、中断和 DMA;协议层做拆帧与校验;业务层做控制决策;双核之间用共享内存 + 同步机制解耦,避免两边直接耦合业务细节。分层的目的不是好看,而是让「换一个通信介质不用动业务逻辑」「业务规则变更不用碰驱动」,出问题时也能快速定位在哪一层。
volatile 必须用的场景:三类。① 被中断或 DMA 改写、又被主循环读取的共享变量——典型是中断里置位、主循环 while(!flag); 等待的标志位,不加 volatile 编译器可能把 flag 优化进寄存器变成死循环;② 映射到寄存器的内存,如 volatile uint32_t *reg = (uint32_t *)0x40000000;,值可能被硬件改变,且连续两次写同一地址不能被合并;③ DMA 异步改写的数据,如 DMA 完成标志和 DMA 缓冲区。要注意 volatile 只保证「每次访问都真的去内存读写、不缓存不合并不删除」,不提供原子性、不提供内存屏障、也不等于线程安全:多字节共享变量仍有读改写竞态,需要关中断、临界区或原子操作;DMA 缓冲区还要考虑 cache 一致性(clean/invalidate 或放在非 cache 区)。
指针数组与数组指针:char *table[] 是指针数组——数组元素都是指针,工程里常用来挂命令表、字符串表、驱动函数表,遍历时按下标取到指针再调用;int (*p)[N] 是数组指针——p 指向一个含 N 个 int 的数组,常用于按行处理二维缓冲,p + 1 会跳过整整一行(N × sizeof(int) 字节)。区别的关键是优先级和步长:[] 优先于 *,所以 char *table[] 先结合成数组,元素类型是 char *;(*p)[N] 因为括号,p 先是指针,指向的东西是数组。易混的还有 int *p[10](指针数组,10 个 int 指针)与 int (*p)[10](数组指针,指向含 10 个 int 的数组)。