指针和引用在运行时性能完全一样,编译优化后生成的汇编指令基本一致,差异仅存在于未优化构建或特殊语义场景,本质是抽象与实现分离的问题。

指针和引用在运行时性能完全一样
编译器优化后,int& 和 int* 在绝大多数场景下生成的汇编指令一模一样——它们都只是对同一块内存地址的间接访问。所谓“引用更快”或“指针更慢”的说法,是把语法糖当成了运行时开销。
关键点在于:引用不是对象,不占存储空间;但它的实现机制就是通过地址传递,底层和指针共享同一套寻址逻辑。只要没禁用优化(如 -O0),两者反汇编出来的 mov、lea 指令几乎无法区分。
- 启用
-O2后,void f(int& x)和void f(int* x)的函数体若只做*x = 42,生成的机器码通常都是单条mov DWORD PTR [rdi], 42 - 引用不能重绑定、不能为空,这些是编译期约束,不产生运行时检查;而指针的空值判断(如
if (p) {...})才是真·额外分支 - 结构体成员含引用时(如
struct S { int& r; };),该结构体大小仍可能等于指针大小(sizeof(S) == sizeof(void*)),但它本身不可赋值、不可memcpy
什么时候引用会比指针多出指令?
仅出现在未优化构建或特殊语义场景中,且差异来自语言规则而非“引用本质慢”。典型情况:
-
-O0下,引用参数可能被编译器保守地分配栈空间并存入地址(模拟“左值”语义),多出几条lea+mov;指针参数则直接进寄存器 - 返回局部变量的引用(如
int& bad() { int x=0; return x; })触发未定义行为,某些调试模式下编译器可能插入ud2中断指令用于捕获——这不是引用慢,是代码非法 - 模板推导中,
T&可能导致引用折叠(int&& & → int&),而指针无此机制;但这影响的是类型系统,不改变最终指令
面试里问“哪个快”,实际在考什么?
这个问题本质是筛选是否理解「抽象与实现分离」。面试官想听的不是汇编行数对比,而是:
立即学习“C++免费学习笔记(深入)”;
- 知道引用是别名(alias),不是实体;指针是对象,可修改、可为空
- 明白性能瓶颈从来不在「用引用还是指针」,而在缓存局部性、是否触发别名分析失败(如
int* __restrict__)、是否引起不必要的拷贝 - 能指出真正影响性能的操作:比如用
std::vector避免复制 vs 用& std::vector却忘了判空,后者可能因崩溃重启反而“最慢”*
如果现场被要求看汇编,直接用 godbolt.org 贴两段带 -O2 的代码对比,重点观察 call 前后的寄存器使用和内存访问模式,而不是数指令条数。
别掉坑:引用不是零成本,但成本不在解引用
引用真正的代价藏在生命周期管理和类型系统里:
- 延长临时对象生命周期(如
const std::string& s = "hello" + "world";)会隐式创建匿名对象并绑定,这有构造/析构开销,和指针无关 - 函数参数用
const T&接收大对象是对的,但若T是std::vector<:string>,引用本身不慢,慢的是后续遍历时的指针跳转和缓存不友好 - 跨模块传递引用时,若头文件未内联函数定义,链接时可能因 ODR(One Definition Rule)问题导致意外拷贝——这也不是引用的问题,是构建配置错误
真正该花时间优化的,永远是数据布局、访问模式和内存分配策略,而不是在 & 和 * 之间反复横跳。









