嵌入式面试基础 20 题:指针、中断、RTOS 与启动流程
- 轮次
- 面试合集
- 时间
- 2026-10
- 来源
- 牛客网
《面试题目》
- 指针和数组有什么区别?
- 堆和栈有什么区别?
static关键字有哪些常见用法?- 为什么需要
volatile? const和volatile能一起用吗?- 大小端是什么?怎么判断?
- 结构体内存对齐是怎么回事?
- 中断和轮询怎么选?
- 中断服务函数里能做什么,不能做什么?
- 关中断、临界区、互斥锁有什么区别?
- GPIO 推挽输出和开漏输出有什么区别?
- UART、SPI、I2C 怎么对比?
- DMA 解决什么问题?有什么风险?
- 什么是硬实时和软实时?
- 任务调度里,抢占式和协作式有什么区别?
- 信号量和互斥锁怎么区分?
- 什么是优先级反转?怎么缓解?
- 复位后到
main之前,大概会发生什么? - 野指针、悬空指针、内存泄漏有什么区别?
- 看门狗是干什么的?为什么不能乱喂?
《参考解析》
指针和数组的区别,面试官想听的是“类型”和“退化”。 数组是一块连续对象,有确定大小,sizeof(arr) 是总字节数;数组名在多数表达式里退化成指向首元素的指针,但数组本身不是指针变量,不能重新赋值。作为函数参数时,数组形参会退化成指针,长度信息丢失,所以必须额外传长度。堆和栈的区别则要落到生命周期和管理方式:栈由编译器和调用约定自动分配释放,速度快、空间有限,局部大数组和深递归容易溢出;堆用 malloc/free 或 RTOS 的堆管理,灵活但会碎片化,常见问题是泄漏、重复释放和越界。嵌入式里还要区分主栈、任务栈和中断栈。
volatile 只保证“每次都从内存读”,不保证原子和线程安全。 它适合硬件寄存器、中断与主循环共享的标志、以及可能被外部修改的变量;它不会阻止编译器重排所有指令,也不能替代锁或原子操作。const 和 volatile 可以一起用,典型是只读状态寄存器:程序不许写,但硬件会改变它的值。static 修饰局部变量时延长生命周期、只初始化一次;修饰全局变量或函数时限制链接作用域,让符号只在当前编译单元可见。结构体对齐则是编译器按成员对齐要求插 padding,协议解析时没对齐好就会错位,#pragma pack 能改布局但可能牺牲访问效率。
中断和并发的答题要落到“上下文”和“最坏路径”。 ISR 应该短:清标志、搬数据、给信号量/队列、置事件;不要在 ISR 里做长计算、阻塞等待、大量打印或动态申请。printf 可能带锁、走缓冲、触发总线访问,在中断里容易拖长关中断时间甚至死锁。关中断是屏蔽中断,影响面大,只能很短;临界区是保护共享资源的一小段代码,实现可能是关调度、关中断或加锁;互斥锁用于任务间互斥,通常支持优先级继承,不能在 ISR 里随便用。信号量偏“事件计数/资源数量通知”,互斥锁偏“所有权保护”,拿信号量当互斥锁用是经典误用。
优先级反转是实时系统的高频追问。 低优先级任务持有锁,高优先级任务等锁,中优先级任务又抢占了低优先级任务,结果高优先级任务被间接拖慢。缓解手段包括优先级继承(持锁者临时提升到等待者的优先级)、优先级天花板、缩短持锁时间、理顺资源层级。调度方面,抢占式让高优先级任务就绪后立即打断低优先级任务,实时性更好;协作式靠任务主动让出 CPU,实现简单但一个坏任务能卡住系统。硬实时是超时即失败,软实时是超时只降体验,有 RTOS 不等于硬实时,关键看最坏执行时间和阻塞路径。
启动流程和看门狗是“系统观”题。 复位后先取复位向量、设置栈顶、跳到复位处理,初始化 .data(从 Flash 搬到 RAM)、清 .bss、准备 C 运行时,然后才进 main;Bootloader、链接脚本和启动文件的问题都会落在这段。看门狗用于系统失控后强制复位,正常路径要按时喂狗,证明关键任务还活着;在高优先级定时中断里无脑喂狗是典型错误,因为主业务死循环了系统也不会复位。野指针是未初始化或指向非法地址,悬空指针是对象已释放但指针还留着旧地址,内存泄漏是申请后不释放;三者在嵌入式里常表现为跑久了重启、偶发 HardFault 或任务栈耗尽。