& 只能作用于有确定内存位置的左值。函数返回的临时对象、字面量、未分配栈空间的局部变量等右值不可取地址;引用取地址得到绑定对象地址;类型必须严格匹配,含const与层级;数组名与&数组名类型不同;取地址不触发构造,但需确保对象生命周期有效。

用 & 取地址时,变量必须有确定的内存位置
不是所有“看起来能取地址”的东西都能用 & 拿到地址。比如函数调用返回的临时对象、字面量、寄存器优化后的局部变量(未取地址前可能根本没分配栈空间),编译器会直接报错:error: lvalue required as unary '&' operand。
实操建议:
-
&只能作用于左值(lvalue),也就是有名字、能被取地址的实体,比如命名变量、数组元素、解引用后的指针(&*p) - 函数返回值如果是按值返回(如
int foo() { return 42; }),&foo()非法;但若返回引用(int& bar() { static int x; return x; }),&bar()合法 - 结构体成员若被声明为
mutable或处于const对象中,仍可取地址——&obj.member不受const限制,因为地址本身是只读的,不等于不允许访问该地址
取地址后赋给指针,类型必须严格匹配(含 const 与引用)
C++ 的类型系统对指针类型很较真。&x 的类型不是笼统的“地址”,而是 int*、const double* 这样的完整类型。忽略 const 或误加 & 层级,编译器立刻拒绝。
常见错误现象:
立即学习“C++免费学习笔记(深入)”;
-
int x = 1; const int* p = &x;✅ 合法(非常量 → 常量指针) -
const int y = 2; int* q = &y;❌ 报错:不能丢弃const -
int z = 3; int** r = &&z;❌ 错误:&z是int*,再取地址得int**,但&&z是非法语法(&&是右值引用运算符) - 数组名退化为指针后,
&arr和arr类型不同:int arr[5];中,arr类型是int*,而&arr是int(*)[5]—— 这个区别在传参给期望接收“整个数组地址”的函数时特别关键
对引用变量取地址,得到的是它绑定对象的地址,不是引用本身的地址
C++ 标准规定:引用不是对象,没有独立存储空间。所以 &ref 不是取“引用变量”的地址(它压根没地址),而是取它所引用的那个原始对象的地址。
使用场景和陷阱:
-
int a = 10; int& r = a; assert(&r == &a);—— 断言恒成立 - 别指望通过
&r判断引用是否“重绑定”过,它永远等于最初绑定对象的地址 - 调试时如果看到两个引用变量的
&结果相同,不代表它们是同一个引用,只说明它们绑定了同一对象(比如都绑了a) - 结构体里含引用成员时,整个结构体无法取地址?不,可以;但引用成员本身不能单独取地址(
&s.ref_member合法,结果仍是所引用对象的地址)
取地址运算符 & 不触发构造或拷贝,但要注意生命周期
& 是纯编译期操作,不生成任何运行时代码,也不调用任何构造/析构函数。但它把一个对象“暴露”给了指针,后续行为就全靠程序员把控了。
容易踩的坑:
- 对函数内局部变量取地址并返回其指针:
int* bad() { int x = 42; return &x; }——x出作用域即销毁,指针悬空,后续解引用是未定义行为 - 对临时对象取地址(即使编译器允许,比如某些版本的 GCC 对
&T{}放宽):生命周期只到完整表达式结束,存下来必出问题 - lambda 表达式默认不捕获时,
&[]{}是非法的(无名 lambda 是右值);但如果声明为变量,auto f = []{}; &f;是合法的,得到的是闭包对象的地址(注意:闭包类型不可名状,但地址可用void*存)
真正麻烦的从来不是怎么写 &,而是写完之后——那个地址指向的东西,还在不在、能不能碰、有没有权限碰。








