第4章 技术基础:C语言与单片机架构
面试问答卡片
问题:int arr[5]; int *p = arr; p+1 与 &arr+1 的区别是什么?
考察点:指针算术运算本质、数组类型退化、指向数组的指针
回答框架:① 明确p是int*,arr退化为int*;② p+1跳过一个int(4字节);③ &arr是int(*)[5],&arr+1跳过整个数组(20字节)。
示例回答:p+1指向arr[1];&arr+1指向arr起始地址+20字节处,即数组末尾之后。本质因为指针类型决定了偏移步长。
可能追问:能用*(&arr+1)访问什么?数组名作为函数参数时退化规则?
问题:在STM32裸机程序中,使用malloc分配内存需要注意什么?
考察点:嵌入式内存堆大小、碎片、堆栈冲突、RTOS环境差异
回答框架:① 确认启动文件heap大小是否足够;② 避免频繁分配释放导致碎片;③ 中断中不可调用malloc(非可重入);④ 推荐使用静态分配或内存池。
示例回答:裸机下malloc默认堆大小由启动文件定义,需检查。我通常在定时任务预处理阶段一次性分配,避免运行时碎片。若用FreeRTOS,改用pvPortMalloc。
可能追问:如何调试内存泄漏?实现一个简单内存池需要考虑什么?
问题:请用位运算实现:将寄存器REG的第3位(bit3)置1,第5位清零,并保持其他位不变。
考察点:位与(&)、位或(|)、取反(~)的熟练度,寄存器读-改-写安全性
回答框架:① 置位:REG |= (1<<3);② 清零:REG &= ~(1<<5);③ 强调寄存器可能受硬件影响,必要时关中断。
示例回答:REG |= (1<<3); REG &= ~(1<<5); 若REG是硬件自动更新寄存器,应先读入临时变量再操作,最后写回。
可能追问:如何用宏定义封装寄存器操作?bit-band操作是什么?
项目案例:智能传感器数据采集模块
| 项目维度 | 内容 |
|---|---|
| 项目名称 | 多路温度/湿度传感器采集与无线传输模块 |
| 业务背景 | 某工厂环境监测系统,需实时采集12个传感器数据,通过SPI读取,经LoRa发送至上位机。 |
| 规模指标 | 传感器12路,采样周期100ms,数据帧大小32字节,LoRa速率2.4kbps,MCU为STM32L051(64KB Flash, 8KB RAM) |
| 技术栈 | STM32 HAL库、SPI、DMA、定时器中断、位域结构体、自定义协议、环形缓冲区 |
| 核心难点 | ① 8KB RAM下需分配6个环形缓冲区,避免动态内存分配;② SPI DMA中断与定时器中断优先级冲突导致丢包;③ 寄存器位操作实现协议打包时比特对齐。 |
| 面试高频追问 | 如何用位域结构体定义协议帧?DMA中断和定时器中断优先级如何设置?如何定位环形缓冲区溢出? |
| 可迁移回答话术 | “在RAM受限场景,我习惯使用静态环形缓冲+全局位域结构体,通过寄存器直接操作控制状态机。调试时借助GPIO翻转输出关键事件时间戳,结合逻辑分析仪定位中断延迟。” |
REG |= (1< 并解释读-改-写风险,且主动提到关中断保护、位域结构体、volatile关键字,是高分回答。