百度 AI Infra 校招 一面
Q: C/C++中的内存区是怎么划分的?
程序加载到虚拟内存后,地址空间被划分为以下区域(以 Linux x86-64 为例,从低地址到高地址):
代码段(Text/Code):存放编译后的机器指令。只读+可执行权限,多个进程运行同一程序可共享此段。地址通常从 0x400000 开始。
数据段(Data):已初始化的全局变量和静态变量。可读写,内容从可执行文件中加载。
BSS 段:未初始化(或初始化为 0)的全局/静态变量。不占用可执行文件空间(只记录大小),程序加载时 OS 将其清零映射。
堆区(Heap):
malloc/new动态分配的内存。从 BSS 段结束处向高地址增长。通过brk()(小块)和mmap()(大块>=128KB)分配。用完需手动释放。栈区(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 | vector<int> v; // vector对象在栈上(24B),元素在堆上 |
两种情况下元素的存储方式完全相同(堆上连续内存),区别仅在于 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: 手撕:二叉树镜像翻转?
(编程题)