go中&用于获取可寻址变量的内存地址,结果为对应类型指针;字面量、map元素、函数返回值等不可寻址值禁止取地址,否则编译报错。

Go 中 & 取地址符的基本用法
Go 的 & 操作符用于获取变量的内存地址,结果类型是对应类型的指针(如 *int)。它只能作用于「可寻址」的值——也就是有固定内存位置的变量,不能对字面量、函数调用返回值、map 元素(除非先赋给局部变量)、channel 接收表达式等直接取地址。
-
&x合法:当x是声明过的变量(var x int或x := 42) -
&42非法:字面量不可寻址,编译报错cannot take the address of 42 -
&m["key"]非法:map 元素在 Go 中不保证可寻址,编译报错cannot take the address of m["key"] -
&s[0]合法:切片、数组元素可寻址(只要s本身可寻址)
为什么 & 有时编译失败?常见不可寻址场景
Go 编译器对“可寻址性”检查很严格,不是所有看起来像变量的东西都能取地址。核心原则是:该值必须有稳定、可预测的内存位置,且生命周期可控。
- 函数返回值(即使返回的是局部变量副本):
&fmt.Sprintf("x")❌ 报错cannot take the address of fmt.Sprintf("x") - struct 字段访问链中含不可寻址中间项:
&(s.f).g如果s.f是值拷贝(比如f是非指针字段),则整个表达式不可寻址 - interface{} 类型变量的底层值:
var i interface{} = 42; &i得到的是*interface{},不是*int;想取底层值地址,必须先断言并赋给变量:if v, ok := i.(int); ok { p := &v } - range 循环中的迭代变量:
for _, v := range s { &v }总是取到同一个地址(因为v是每次迭代复用的变量),这不是 bug,但常被误用
取地址后传参:指针传递与逃逸分析的关系
用 &x 把变量地址传给函数,看似只是语法动作,但会影响编译器的逃逸分析结果——这直接决定变量分配在栈还是堆上。
- 如果函数参数是
*T且该指针被“逃逸”(比如存入全局变量、返回、传给 goroutine),那么x会被强制分配到堆,即使它原本很小、生命周期短 - 避免无谓取地址:例如
fmt.Printf("%p", &x)不会逃逸;但store(&x)若store声明为func store(p *int) { globalPtr = p },就会导致x逃逸 - 验证方式:加
-gcflags="-m"编译,看输出是否含... escapes to heap
struct 字段取地址的边界情况
对 struct 字段取地址时,不仅要看字段本身类型,还要看 struct 实例是否可寻址。
立即学习“go语言免费学习笔记(深入)”;
-
type S struct{ x int }; var s S; &s.x✅ 安全 -
func get() S { return S{1} }; &get().x❌ 编译失败:临时 struct 值不可寻址 -
type T struct{ y *int }; t := T{&x}; &t.y✅ 得到**int,但注意这是指针的地址,不是*int指向的值的地址 - 嵌入字段同理:
type E struct{ S }; e := E{}; &e.x✅ 等价于&e.S.x,前提是e可寻址
最易忽略的一点:取地址操作本身不改变原值,但拿到指针后任何解引用写操作都会影响原变量——这点在并发或闭包捕获中尤其容易出问题。









