砺算科技嵌入式软件一面:C/C++ 基础、内存布局与位掩码
- 轮次
- 一面
- 结果
- 等待结果
- 时间
- 2026-10
- 来源
- 牛客网
《面试题目》
- 嵌入式系统启动较慢时,如何分阶段埋点、定位并优化初始化耗时?
- 什么是零拷贝?在多硬件视频处理链路中体现在哪些阶段?
- C 和 C++ 有哪些主要区别?静态多态与动态多态如何区分?
- C/C++ 源码到可执行文件经过哪些阶段?
- 程序的常见内存分区有哪些?
- 栈和堆一般分别存放什么?
malloc与new有什么区别?- 虚函数通常如何实现?
- 为什么构造函数不能是虚函数?
- 引用与指针有什么区别?
- C++11 及后续标准有哪些常用特性?
- STL 常用容器有哪些?
vector与list在存储布局和操作开销上有什么区别?- 手撕:给出起始位和停止位,返回一个 uint64 掩码,区间内为 1,其余为 0。
《参考解析》
启动优化要先有分阶段时间线。 从复位向量、BootROM、Bootloader、内核解压、驱动 probe、根文件系统挂载到应用启动,每个阶段打时间戳,先找出占比最大的那一段。常见手段包括裁剪内核和驱动、把不必要的初始化改成按需加载、并行 probe、缩短延时和超时、优化文件系统挂载、把大资源放到后台加载。埋点本身要轻量,不能因为打日志拖慢启动;生产环境可以保留一组可开关的 tracepoint。回答时最好给一个真实例子:比如某个驱动 probe 里有固定 sleep,改成异步后启动缩短了多少毫秒。
零拷贝是减少“CPU 参与的数据搬运”,不是完全没有拷贝。 传统 read/write 要在内核缓冲和用户缓冲之间复制,mmap 把内核缓冲映射到用户态,sendfile/splice 让数据在内核内部流转,DMA 则让设备直接读写内存。视频链路里,采集设备通过 DMA 写入缓冲区,驱动把 buffer 交给 V4L2,应用用 mmap 或 DMA-BUF 把同一块内存传给编码器/显示设备,避免每级都 memcpy。零拷贝的代价是生命周期和同步更复杂:谁分配、谁持有、什么时候可以回收、CPU 和设备之间怎么做 cache 同步,都要设计清楚。
C 与 C++ 的区别、编译流程和内存布局是连在一起考的。 C 更贴近硬件、没有类和模板,C++ 多了 RAII、模板、异常、命名空间和运行期多态;静态多态靠重载/模板在编译期决议,动态多态靠虚函数表在运行期派发。编译流程是预处理、编译、汇编、链接;内存分区通常分为代码段、只读数据段、已初始化数据段、未初始化数据段(BSS)、堆和栈,局部变量在栈上、动态内存在堆上,全局/静态变量在数据段或 BSS。malloc 只分配原始内存、不调用构造,new 分配并构造、失败抛异常(nothrow 版本返回空);free 和 delete 不能混用,数组要用 new[]/delete[]。
虚函数与构造析构的规则要能解释原因。 有虚函数的类通常有一个 vptr 指向虚表,虚表里按声明顺序存放虚函数地址;派生类覆写后替换对应槽位,多继承时还要做 this 指针调整。构造函数不能是虚函数,因为对象还没构造完、vptr 还没建立,虚调用无法派发;析构函数在作为基类时必须是虚函数,否则通过基类指针删除派生对象只会析构基类部分。引用必须初始化、不能重新绑定,指针可以为空、可以改指向;引用在语义上更像“别名”,底层通常也用地址实现,但不要把它当成语法糖到处讲。
位掩码手撕要处理边界。 最直接的写法是 ((1ULL << (stop - start + 1)) - 1) << start,但要注意 stop - start + 1 == 64 时左移 64 是未定义行为,需要单独返回 ~0ULL;start > stop、stop >= 64 也要校验。如果区间可能跨越 64 位,就要拆成两段或改用更通用的位图。写完后用 start=0, stop=0、start=0, stop=63、start=63, stop=63 三个用例手算一遍,确保没有差一错误。