百度 AI Infra 校招 一面


Q: C/C++中的内存区是怎么划分的?

程序加载到虚拟内存后,地址空间被划分为以下区域(以 Linux x86-64 为例,从低地址到高地址):

  1. 代码段(Text/Code):存放编译后的机器指令。只读+可执行权限,多个进程运行同一程序可共享此段。地址通常从 0x400000 开始。

  2. 数据段(Data):已初始化的全局变量和静态变量。可读写,内容从可执行文件中加载。

  3. BSS 段:未初始化(或初始化为 0)的全局/静态变量。不占用可执行文件空间(只记录大小),程序加载时 OS 将其清零映射。

  4. 堆区(Heap)malloc/new 动态分配的内存。从 BSS 段结束处向高地址增长。通过 brk()(小块)和 mmap()(大块>=128KB)分配。用完需手动释放。

  5. 栈区(Stack):局部变量、函数参数、返回地址、callee-saved 寄存器。从高地址向低地址增长,默认 8MB 限制(ulimit -s 可调)。分配释放极快(移动 RSP 即可)。

补充:堆栈之间有大量未映射空间(ASLR 随机化)和 mmap 区域(用于动态库和大块分配)。


Q: new和malloc的区别?

维度 new malloc
本质 C++ 运算符 C 标准库函数
返回类型 具体类型指针(无需转换) void*(需强制类型转换)
大小计算 自动根据类型计算 手动指定字节数
构造/析构 调用构造函数/析构函数 不调用(纯内存分配)
失败行为 抛出 std::bad_alloc 异常 返回 NULL
可重载 可以(operator new) 不可以
配对释放 delete/delete[] free()
底层实现 通常内部调用 malloc + 构造 直接系统调用

使用建议:C++ 中优先用 make_unique/make_shared > new > malloc。现代 C++ 几乎不需要直接使用 new/malloc(RAII 智能指针管理)。


Q: vector在内存中怎么存的?new的vector存储方式一样吗?

vector 对象本身是一个包含三个指针的结构体(24 字节 on x86-64):

  • _start:指向元素数组的起始位置
  • _finish:指向最后一个元素之后(size = finish - start)
  • _end_of_storage:指向分配的内存末尾(capacity = end - start)

元素始终存储在堆上的连续内存中(保证随机访问 O(1) 和缓存友好性)。

两种声明方式的区别

1
2
vector<int> v;              // vector对象在栈上(24B),元素在堆上
vector<int>* pv = new vector<int>; // vector对象也在堆上,元素在另一块堆内存

两种情况下元素的存储方式完全相同(堆上连续内存),区别仅在于 vector 对象本身(24B 的 header)的位置。实践中几乎不需要 new vector——直接在栈上或作为成员变量声明即可。


Q: 给定卷积参数,计算时间复杂度?

标准 2D 卷积的计算复杂度:

FLOPs = 2 * C_out * C_in * K_h * K_w * H_out * W_out

(其中因子 2 是因为每个乘法配一个加法,即乘加运算 FMA)

分解理解

  • 输出有 C_out * H_out * W_out 个元素
  • 每个输出元素需要做 C_in * K_h * K_w 次乘加
  • 总计:C_out * H_out * W_out * C_in * K_h * K_w 次 MAC(乘加)

实际执行时间取决于:(1) 硬件峰值 TFLOPS;(2) 算法实现的效率(Im2col/Winograd/Direct);(3) 数据搬运效率(memory-bound vs compute-bound)。相同 FLOPs 的卷积在不同 shape 下实际延迟可能差数倍。


Q: 手撕:二叉树镜像翻转?

(编程题)