蔚来 嵌入式 一面 面经

1. 说一下 volatile 的作用,它能保证线程安全吗?

答:

volatile 的核心作用是保证变量的可见性,并且在一定程度上禁止指令重排,但它不能保证复合操作的原子性,所以一般不能单独用来保证线程安全。

  1. 可见性
  • 一个线程修改了 volatile 变量后,其他线程能够立刻看到最新值。
  • 它适合做状态标志位,比如“设备初始化完成”“接收到数据”等。
  1. 禁止指令重排
  • 编译器和 CPU 在优化时可能会调整指令顺序。
  • volatile 会在读写时加入内存屏障,避免关键步骤被乱序执行。
  1. 不能保证原子性
  • count++ 这种操作,本质上是“读-改-写”三步,不是原子操作。
  • 即使 countvolatile,多个线程同时执行时仍然可能出错。
  1. 使用场景
  • 中断与主循环共享的标志位。
  • 多线程中的状态通知。
  • 配置参数刷新。
  1. 不适合的场景
  • 计数器累加。
  • 多线程共同修改复杂结构体。
  • 需要加锁保护的临界区。

一句话总结:volatile 更适合“通知”,不适合“竞争修改”。

2. 详细说一下函数调用时,参数和局部变量一般放在哪里?

答:

这个问题本质上是在考察程序内存布局函数调用过程

  1. 参数和局部变量通常放在栈上
  • 函数被调用时,会创建一个栈帧。
  • 形参、返回地址、部分寄存器现场、局部变量,通常都在这个栈帧中保存。
  1. 栈的特点
  • 由编译器自动分配和释放。
  • 访问速度快。
  • 空间相对较小。
  • 生命周期跟随函数调用过程。
  1. 特殊情况
  • static 局部变量不在栈上,而是在全局/静态区。
  • 如果局部变量特别大,可能导致栈溢出。
  • 某些优化情况下,局部变量可能直接放在寄存器里,不一定真的落到栈中。
  1. 为什么不能返回局部变量地址
  • 局部变量属于当前函数栈帧。
  • 函数返回后,栈帧被销毁,地址虽然还在,但内容已经不可靠。
  1. 嵌入式里要注意什么
  • 任务栈一般比较小,不能在函数中定义过大的局部数组。
  • 中断函数也要尽量减少栈占用。

一句话总结:普通局部变量大多在栈上,static 局部变量不在栈上。

3. malloc 申请的内存和数组定义出来的内存,有什么区别?

答:

这道题主要考察堆、栈、生命周期、管理方式

  1. 分配位置不同
  • malloc 申请的内存通常在堆上。
  • 普通局部数组通常在栈上。
  • 全局数组或 static 数组在静态存储区。
  1. 生命周期不同
  • malloc 申请后,必须手动 free
  • 局部数组在函数结束后自动销毁。
  • 全局数组在程序整个运行期间都存在。
  1. 大小灵活性不同
  • malloc 可以运行时动态决定大小。
  • 普通数组大小一般在编译时确定。
  1. 使用风险不同
  • malloc 可能申请失败。
  • 可能发生内存泄漏、野指针、重复释放、碎片化。
  • 栈数组虽然简单,但空间太大可能栈溢出。
  1. 嵌入式开发中的考虑
  • 小型 MCU 中通常慎用动态内存分配。
  • 因为堆空间有限,且长期运行后容易产生碎片。
  • 很多项目更倾向于静态分配或内存池。

一句话总结:malloc 更灵活,但管理更复杂;数组更简单,但大小和生命周期受限制。

4. 为什么很多驱动开发都强调“寄存器操作要谨慎”?

答:

因为寄存器直接对应硬件行为,写错一位,可能导致整个外设异常。

  1. 寄存器和普通变量不同
  • 普通变量只是软件数据。
  • 寄存器往往直接控制时钟、IO、电平、DMA、串口发送等硬件动作。
  1. 读改写风险
  • 某些寄存器中的位是状态位、清零位、自清位。
  • 如果直接“先读后改再写”,可能误清除状态或覆盖其他位。
  1. 位操作要准确
  • 常用掩码方式设置和清除某些位:
  • reg |= mask
  • reg &= ~mask
  • 避免影响无关位。
  1. 要注意时序
  • 某些寄存器必须按固定顺序配置。
  • 比如先开时钟,再配模式,再使能模块。
  • 顺序错了,外设可能不工作。
  1. 要注意 volatile
  • 寄存器地址通常会定义为 volatile
  • 防止编译器优化掉读写操作。
  1. 中断和并发问题
  • 主程序和中断都访问同一个寄存器时,要考虑同步问题。
  • 某些操作需要临界区保护。

一句话总结:寄存器操作不是简单赋值,而是在直接控制硬件,所以必须精确、谨慎、按时序来。

5. 说一下 Bootloader 和应用程序的区别?

答:

这道题在车载和嵌入式面试里很常见。

  1. Bootloader 是什么
  • 上电后最先运行的一段程序。
  • 负责基础初始化、程序校验、升级、跳转到应用程序。
  1. Application 是什么
  • 就是业务功能程序。
  • 例如电机控制、通信协议、传感器采集、人机交互等。
  1. 二者分工不同
  • Bootloader 更关注启动和升级。
  • Application 更关注具体业务逻辑。
  1. Bootloader 常见功能
  • 初始化最基本的硬件。
  • 检查应用程序是否有效。
  • 接收升级包,比如 CAN、串口、以太网升级。
  • 擦写 Flash。
  • 完成程序跳转。
  1. 为什么要分开
  • 防止应用程序损坏后设备彻底无法启动。
  • 方便在线升级和故障恢复。
  • 提高系统可维护性。
  1. 跳转时要注意
  • 关闭中断。
  • 设置 MSP。
  • 设置向量表偏移。
  • 跳到应用程序复位入口。

一句话总结:Bootloader 负责“启动和升级”,应用程序负责“具体功能”。

6

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

嵌入式面试八股文全集 文章被收录于专栏

这是一个全面的嵌入式面试专栏。主要内容将包括:操作系统(进程管理、内存管理、文件系统等)、嵌入式系统(启动流程、驱动开发、中断管理等)、网络通信(TCP/IP协议栈、Socket编程等)、开发工具(交叉编译、调试工具等)以及实际项目经验分享。专栏将采用理论结合实践的方式,每个知识点都会附带相关的面试真题和答案解析。

全部评论

相关推荐

04-07 13:43
四川大学 Java
点赞 评论 收藏
分享
牛客62533758...:华为不卡双非,而是卡院校hhhh
点赞 评论 收藏
分享
评论
点赞
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务