汇编层面不存在引用,它被编译器直接替换为被引用对象的地址或值,不分配栈空间、不生成取地址指令;指针则占用内存并需显式解引用,二者语义不同但优化后汇编可能一致。

引用在汇编层面根本不存在
引用不是独立的内存实体,编译器在生成汇编代码时会直接将其替换为被引用对象的地址或值,不分配额外栈空间,也不产生 lea 或 mov reg, [addr] 这类“取地址再解引”的指令序列。比如 int& r = x; 后对 r 的所有读写,基本等价于直接操作 x——除非涉及生命周期延长(如绑定到临时对象),此时编译器可能隐式转为 const 指针并延长临时对象栈帧,但这个指针对用户完全不可见。
指针变量本身占内存,且必须显式解引用
每个指针变量(如 int* p)都占用一块确定大小的存储(通常是 8 字节 on x64),它存的是目标对象地址。你在 C++ 中写的 *p = 5;,对应汇编里至少两步:先从指针变量所在位置读出地址(mov rax, [rbp-8]),再向该地址写值(mov DWORD PTR [rax], 5)。这多出来的间接层带来开销,也允许运行时修改指向(p = &y;),而引用一旦绑定就不能改。
为什么不能取引用的地址
&r 看似在取引用地址,实际是取它所绑定对象的地址;编译器禁止 &r 返回“引用自身的地址”,因为引用没有自己的地址。尝试 int** pp = &r; 会编译失败——&r 类型是 int*,不是 int**。反观指针:&p 是合法的,类型是 int**,对应汇编中一次 lea rax, [rbp-8],即获取指针变量自身在栈上的位置。
优化场景下二者生成的汇编可能完全一致
当开启 -O2 且无副作用时,编译器常把引用和直接变量一视同仁优化。例如:
立即学习“C++免费学习笔记(深入)”;
void f(int& r) { r++; }
void g(int* p) { (*p)++; }
两者在内联后很可能都编译成一条 inc DWORD PTR [rdi](假设参数传入 rdi)。区别只在语义约束:引用强制绑定、不可空、不可重绑定;指针可以为空、可重赋值、可算术运算。这些规则由编译器在前端检查,不体现在最终机器码上。
真正容易被忽略的是:引用的“不可空”只是语言契约,底层没硬件或指令级保障;若通过 reinterpret_cast 或内存篡改制造一个绑定到非法地址的引用,解引用时照样触发段错误——和野指针一样,只是你更难在源码里看出破绽。










