(嵌入式面经)第11章 20+公司面经杂谈(一):华为、博世、理想、韶音

本篇涉及的所有问题概要:大家可以试试在看参考答案前,提前尝试解答,以便明确自身知识点的不足部分!

1. TCP & UDP了解吗,简单说一下,有什么区别?

2. 好的,那你了解粘包跟拆包吗,这两个协议会出现这个情况吗,为什么呢?

3. 了解编译器优化吗?

4. 刚才你有说到内联函数,你来讲讲它的作用,以及与普通函数的区别,有不建议使用的时候吗?

5. 那么有没有碰到过禁止编译器优化的场景,能举例吗?

6. 看门狗了解吗,说一说独立看门狗与窗口看门狗?

7. 说一说堆(Heap)和栈(Stack)的区别,以及是否自动分配?

8. 重入函数、函数重写、函数重载你了解吗,说一说?

9. 嵌入式开发中,你一般debug会看什么信息?

10. 专栏订阅奖励(支持模仿)——个人创新点问答:我看你在自己设计的FreeRTOS PLUS(我自己取的RTOS名字)内存管理中采用了内存推迟合并策略,这是什么?如何实现的?

---------------------------------------------------------------------------------------------------

1. TCP & UDP了解吗,简单说一下,有什么区别?

TCP(传输控制协议):

  • 面向连接:通信前需要建立连接(三次握手),通信结束时关闭连接(四次挥手)。
  • 可靠传输:数据传输有确认机制、超时重传机制,确保数据可靠性。
  • 有序传输:数据包按发送顺序到达,不乱序。
  • 流量控制与拥塞控制:能动态调整发送速率,避免网络拥堵。
  • 适用场景:网页浏览(HTTP/HTTPS)、文件传输(FTP)、邮件传输(SMTP)等。

UDP(用户数据报协议):

  • 无连接:不需要建立连接,可以直接发送数据包。
  • 不可靠传输:不保证数据包一定送达,不提供确认机制,不进行重传。
  • 无序传输:数据包可能乱序到达。
  • 轻量高效:协议简单,效率高,延迟低。
  • 适用场景:实时通信(视频会议、直播)、网络游戏、DNS查询等对速度敏感但能容忍部分数据丢失的场景。

简单来说:

  • TCP强调可靠性,UDP强调速度和效率。

2. 好的,那你了解粘包跟拆包吗,这两个协议会出现这个情况吗,为什么呢?

粘包与拆包的定义:

粘包:

定义

  • 多个数据包被合并成一个包,接收方无法分辨消息边界。

可能的接收情况

发生原因

  • TCP 发送缓冲区优化:为了减少小数据包的开销,Nagle 算法 可能会将小包合并。
  • TCP 以流方式传输,没有消息边界。
  • 发送方发送的数据 < 接收方 TCP 缓冲区,系统等待数据填满缓冲区后一次性发送。

拆包:

定义

  • 一个数据包被拆成多个小包 进行传输,接收方一次 recv() 可能收到不完整的数据。

可能的接收情况

发生原因

  • 发送方发送的数据 > TCP 缓冲区大小,需要 分片发送
  • 网络传输中的 MTU(最大传输单元)限制,超过 MTU 需要拆分。

TCP与UDP出现的情况:

  • TCP 可能发生粘包或拆包,因为它是 流式传输协议,没有消息边界,数据像流水一样按 字节流 传输。
  • UDP 不会发生粘包或拆包,因为它是 数据报协议,每个 UDP 数据包 有明确的消息边界
  • 如何解决TCP粘包、拆包可能带来的问题:

    (1)固定长度协议

    • 方法:规定 每个数据包固定大小,接收方按固定字节读取。
    • 适用场景:定长结构数据(如传感器数据)。

    优点

  • 解析简单,不需要额外标记消息边界。
  • 缺点

  • 浪费带宽,如果数据不足 1024 字节,仍然要填充。
  • 不适用于变长数据
  • (2)分隔符协议

  • 方法:在数据包间插入特殊 分隔符(如 \n, \0, |),接收方按分隔符拆分数据
  • 适用场景:文本协议(HTTP、Redis)。
    • 接收方

    优点

  • 适用于文本传输。
  • 缺点

    • 数据中包含分隔符时可能误判,需要 转义或编码。

    (3)消息头协议

  • 方法:在 每个数据包前加上消息头(存储数据包长度),接收方 先读取消息头,再根据长度读取数据
  • 适用场景:二进制协议,如 WebSocket、RPC
    • 接收方

    优点

  • 适用于二进制数据,能精确拆分数据包。
  • 缺点

    • 需要额外存储消息头,协议较复杂。

    3. 了解编译器优化吗?

    1. 编译器优化的目的

    编译器优化的主要目标是 提高程序执行效率、减少代码大小,并降低运行时资源消耗,同时 保证代码逻辑正确性

    优化可分为:

    1. 代码级优化:调整代码结构,提高执行速度。
    2. 指令级优化:调整汇编代码顺序,提高 CPU 指令流水线利用率。
    3. 存储优化:减少内存访问,提高缓存命中率。

    2. 编译器优化的主要策略

    编译器在 编译时(compile-time)运行时(runtime) 进行优化,主要包括:

    (1)代码优化

    常量传播(Constant Propagation)

    • 如果变量的值是编译期已知的常量,则用该常量替换变量,减少计算次数。

    优化前

    优化后

    常量折叠(Constant Folding)

    • 编译器提前计算常量表达式,减少运行时计算。

    优化前

    优化后

    死代码消除(Dead Code Elimination)

    • 删除不会被执行的代码,减少程序大小。

    内联展开(Function Inlining)

    • 小函数可以直接展开,避免函数调用的开销。

    优化前

    优化后

    (2)指令优化

    指令重排序(Instruction Reordering)

    • 调整指令执行顺序,提高 CPU 指令流水线利用率。

    优化前

    优化后(可能)

    寄存器分配(Register Allocation)

    • 尽量使用寄存器存储变量,减少内存访问,提高速度。

    优化前

    优化后

    循环展开(Loop Unrolling)

    • 减少循环控制开销,提高 CPU 指令执行效率

    优化前

    优化后

    • 这样可以减少 for 循环的控制开销,提高速度。

    (3)存储优化

    内存对齐(Memory Alignment)

    • 优化数据结构布局,减少 CPU 访问内存的时间

    优化前

    优化后

    这样能 提高 CPU 读取效率,减少缓存行冲突(Cache Miss)。

    缓存优化(Cache Optimization)

    • 提高数据局部性,减少 CPU 访问内存的延迟

    优化前

    优化后(列访问)

    • 减少缓存不命中(Cache Miss),提高 CPU 读取速度。

    3. 现代编译器优化工具

    (1)编译器优化级别

    (2)GCC/Clang 编译器优化选项

  • -O2:启用优化
  • -march=native:针对本地 CPU 优化
  • -flto:启用 链接时优化(Link-Time Optimization)
  • 4. 优化方法总结

    结论

    编译器优化能显著提高程序性能,但可能会引入 并发问题

    指令重排序、寄存器缓存、循环优化 是编译器优化的核心策略。

    使用 -O2-O3 可以让 GCC 进行更高级别优化,提高执行效率

    4. 刚才你有说到内联函数,你来讲讲它的作用,以及与普通函数的区别,有不建议使用的时候吗?

    1. 什么是内联函数?

    内联函数(inline function)是 在编译时展开不执行常规的函数调用 的函数。

    编译器会将函数体直接替换到调用点,避免函数调用的开销

    示例

    编译器优化后

    关键点

    • 没有函数调用的开销(如 push/pop、返回地址)
    • 适用于小函数,提高程序执行效率

    2. 为什么使用内联函数?

    减少函数调用开销

    • 普通函数调用涉及 栈帧创建、参数传递、返回地址保存,而 内联函数避免了这些额外开销

    提高程序运行效率

    • 短小的函数,如 数学运算、逻辑判断,可以 直接在调用点展开,减少 CPU 指令数。

    优化代码分支预测

    • 内联展开可减少跳转,提高 CPU 指令流水线 的执行效率。

    3. 内联函数 vs 普通函数

    4. 什么时候不适合使用内联函数?

    (1)递归函数

    问题

    • 递归调用无法展开,编译器 不会内联
    • 递归会创建新的栈帧,不能优化

    (2)函数体很大

    问题

    • 大函数展开会导致代码膨胀(Code Bloat),增加可执行文件大小。
    • 会影响 CPU 指令缓存(Cache Miss)

    (3)虚函数

    问题

    • 虚函数是通过 vtable(虚函数表)调用的,无法内联。
    • 因为实际调用的函数在运行时决定,所以编译器无法展开。

    5. 现代 C++ 中 inline 的改进

    (1)C++17 inline 变量

    • C++17 允许在类外定义 inline 变量,保证 唯一性

    (2)constexpr 结合 inline

    • C++11 及以上可使用 constexpr 进行编译时计算,替代 inline

    • constexpr 适用于编译期常量计算,性能更优

    结论

    内联函数减少函数调用开销,提高性能,适用于 短小、高频调用的函数

    避免在 递归、虚函数、大型函数 中使用 inline,防止代码膨胀。

    现代 C++ 推荐 constexpr 作为 inline 的替代方案

    5. 那么有没有碰到过禁止编译器优化的场景,能举例吗?

    在某些特殊情况下,我们需要 禁止编译器优化,以确保 正确性、可调试性特定硬件行为

    (关键是答出volatile关键字的作用及应用场景!)

    1. 多线程共享变量

    场景

    • 多线程编程 中,编译器可能会优化掉某些变量的读取和存储,导致 线程无法正确感知变量的变化

    示例(使用 volatile 防止优化)

    volatile 保证 stop_flag 每次都从内存读取,而不会被编译器优化。

    2. 防止指令重排序

    场景

    • 高性能并发代码 中,编译器可能调整指令顺序,导致 CPU 乱序执行,影响多线程同步的正确性。

    示例(使用 memory barrier 防止优化)

    std::memory_order_acquirestd::memory_order_release确保 ready 的写入先于读取,防止 CPU 和编译器重排序

    3. 避免优化延迟计算

    场景

    • 在某些 时间敏感的应用(如定时器、硬件访问),编译器可能优化掉 看似无用的循环,导致代码行为异常。

    示例(使用 volatile 防止空循环优化)

    volatile 确保循环不会被优化掉

    4. 访问硬件寄存器

    场景

    • 嵌入式系统、驱动程序开发 中,访问 I/O 端口、寄存器 需要 禁止编译器优化,否则可能导致硬件操作失败。

    示例(访问硬件寄存器)

    volatileGPIO_PORT 每次都直接访问内存,而不会使用寄存器缓存

    5. 避免调试优化

    场景

    • 在调试模式下,编译器可能会优化掉某些变量,使得 GDB、LLDB 等调试工具无法准确读取变量值

    示例(使用 volatile 变量防止调试优化)

    volatilex 始终存储在内存中,而不会被优化掉,确保 调试时可以正确读取 x

    6. 读取随机数、时间等外部状态

    场景

    • 编译器可能优化掉外部状态读取(如 time()rand(),导致错误的结果。

    示例(确保每次 rand() 都会被调用)

    volatile 确保 rand() 每次都执行,而不会被优化掉

    7. 防止优化特定代码块

    场景

    • 某些情况下,我们需要防止编译器优化整个代码块,例如测试 CPU 性能、精确计时。

    示例(使用 asm volatile 禁止优化)

    asm volatile("" ::: "memory") 作为 memory barrier,确保代码不会被优化掉

    结论

    在多线程、硬件编程、调试、性能测试等场景下,可能需要禁止编译器优化

    使用 volatilestd::atomicmemory barrier 可以防止编译器进行特定优化

    合理使用 volatile,避免编译器优化带来的错误,提高代码的正确性和可维护性! 🚀

    6. 看门狗了解吗,说一说独立看门狗与窗口

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

    作者简介:仅用几个月时间0基础天坑急转嵌入式开发,逆袭成功拿下华为、vivo、小米等15个offer,面试经验100+,收藏20+面经,分享求职历程与学习心得。 专栏内容:这是一份覆盖嵌入式求职过程中99%问题指南,详细讲解了嵌入式开发的学习路径、项目经验分享、简历优化技巧、面试心得及实习经验,从技术面,HR面,AI面,主管面,谈薪一站式服务,助你突破技术瓶颈、打破信息差,争取更多大厂offer。

    全部评论
    为什么嵌入式开发还要问这么多cpp的
    点赞 回复 分享
    发布于 04-01 14:39 新加坡
    博主请问这是自己面试的问题嘛还是整理的
    点赞 回复 分享
    发布于 03-21 14:49 重庆

    相关推荐

    04-10 12:36
    南京大学 Java
    #牛客AI配图神器##找工作如何保持松弛感?#在求职过程中,我也曾多次陷入焦虑、紧张和自我怀疑的情绪漩涡中。但渐渐地我意识到,“松弛感”并不是放弃努力、躺平摆烂,而是一种轻盈自在的心态,一种允许自己试错的态度。毕竟,找工作并非是一场不容许犯错的考试,而是与未来相互探索、彼此了解的双向沟通过程。那么,该如何在求职中保持这种“松弛感”呢?今天,我想聊聊那些让我拥有职场“呼吸感”的小妙招。首先,调整目标,避免内耗。过去,我常常过于执着于某一个岗位或某一家公司,仿佛错过了就会天崩地裂。但慢慢地我发现,我们的目标其实可以灵活调整,不必吊死在一棵树上。我开始把每一次面试当作一次对话和尝试,即使结果并不理想,也能够快速调整状态,减少内耗,轻装上阵。其次,别害怕“暴露真实”。在面试初期,我也曾习惯给自己戴上一层完美的面具,却忽略了企业同样需要的是一个真实、有温度的人。当我尝试在面试中适当地分享一些真诚的想法甚至小缺点时,不仅拉近了与面试官的距离,也让我自己感觉更加轻松自在。再者,给自己设定“呼吸间隔”。曾经密集地投递简历、马不停蹄地赶面试,让我经常处于一种紧绷的状态。后来我尝试给自己设置适当的“休息时间”,比如每周留出一天完全放空,去看看喜欢的电影,或者约朋友聊聊天。这种短暂的暂停不仅没有降低我的效率,反而让我在之后的求职活动中更加从容。最后,建立多维度的自我认可。求职最容易让我焦虑的时刻,就是遭遇拒绝时觉得自我价值被否定。为了避免这种负面情绪,我开始从多个维度去认可自己,比如不断记录自己的成长点,认可自己在过程中付出的努力。当内心建立起强大的自我认同时,我就不会轻易因为一次两次的失利而陷入情绪低谷。记住,找工作是一场旅程,而不是一场赌注。保持松弛感,就是允许自己适当的轻松,用自在的态度去迎接未知的职场未来。
    点赞 评论 收藏
    分享
    AI大模型产品经理的职责和能力要求需要结合技术深度与产品管理的广度。一、职责上1.&nbsp;产品战略与规划-&nbsp;制定大模型产品的长期愿景与落地路径,平衡技术可行性、市场需求和商业价值。-&nbsp;探索垂直场景(如医疗、金融、教育)的应用,定义产品形态(API、SaaS、嵌入式解决方案等)。2.&nbsp;需求洞察与优先级管理-&nbsp;深度理解用户痛点(如企业降本增效需求),转化为技术需求(如模型微调、Prompt工程)。-&nbsp;权衡需求优先级,例如在模型效果(准确率)、成本(算力消耗)和用户体验(响应速度)间找到平衡。3.&nbsp;技术协同与模型迭代-&nbsp;与算法团队合作优化模型性能,参与关键决策(如选择基座模型、调整训练数据分布)。-&nbsp;推动模型迭代闭环,设计评估指标(如任务完成率、幻觉率)并分析用户反馈数据。4.&nbsp;数据与合规治理-&nbsp;构建数据飞轮:设计用户反馈→数据标注→模型优化的链路,确保数据合规(如隐私脱敏、版权审查)。-&nbsp;制定内容安全策略(如敏感词过滤、输出结果审核机制),应对伦理风险(偏见、误导性生成)。5.&nbsp;商业化与生态建设-&nbsp;设计盈利模式(按调用量收费、定制化训练服务),探索生态合作(开发者社区、行业伙伴共建场景)。-&nbsp;推动市场教育,降低用户使用门槛(如提供低代码工具、行业最佳实践案例库)。二、核心能力1.&nbsp;技术理解力-&nbsp;掌握大模型核心概念(Transformer架构、RLHF、LoRA微调),能评估技术方案优劣(如选择开源模型&nbsp;vs&nbsp;自研)。-&nbsp;了解工程约束(推理延迟、显存占用)及优化方向(模型压缩、分布式推理)。2.&nbsp;场景抽象能力-&nbsp;将碎片化需求抽象为通用能力(如客服场景中的“多轮对话管理”模块),提升模型复用性。-&nbsp;设计领域适配方案(如法律场景的术语增强训练、医疗场景的检索增强生成)。 #牛客激励计划#&nbsp;&nbsp;#聊聊我眼中的AI# #产品经理#&nbsp;&nbsp;#聊聊我眼中的AI# #产品每日一题# #牛客AI配图神器#
    点赞 评论 收藏
    分享
    评论
    12
    34
    分享

    创作者周榜

    更多
    牛客网
    牛客企业服务