Go中&操作符仅能对可寻址变量(如局部变量、结构体字段、切片元素)取地址,字面量、函数返回值、map元素等不可寻址值直接取地址会编译报错;反射中需用reflect.ValueOf(&x).Elem()获取可寻址Value再取地址。

Go 里获取变量的指针地址,最直接的方式就是用 & 操作符——但前提是变量「可寻址」;否则编译直接报错,不会等到运行时。
哪些值能用 & 取地址?
只有具备稳定内存位置的变量才能取地址。常见可寻址场景包括:
- 普通声明的局部变量:
var x int→&x合法 - 结构体字段(且结构体本身可寻址):
s := MyStruct{Field: 42}; &s.Field - 切片或数组元素:
s := []int{1,2,3}; &s[0] - 已声明的 map 元素需先赋给临时变量:
v := m["key"]; &v,不能直接&m["key"]
典型不可寻址错误:
-
&42→cannot take the address of 42 -
&fmt.Sprintf("x")→ 返回值不可寻址 -
for _, v := range s { &v }→v是每次迭代复用的栈变量,地址始终相同
reflect.Value 怎么安全拿到指针地址?
反射中想拿到原始变量的地址,关键不是 reflect.ValueOf(x),而是必须传入指针再解一层:
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
立即学习“go语言免费学习笔记(深入)”;
-
reflect.ValueOf(x)得到的是值副本,.Addr()会 panic - 正确路径是:
reflect.ValueOf(&x).Elem()→ 得到可寻址的Value,再调.Addr().Interface()转回*T - 若只需底层地址数值(如日志调试),可用
.UnsafePointer(),但注意它不保证类型安全
示例:
var x int = 123
v := reflect.ValueOf(&x).Elem()
ptr := v.Addr().Interface().(*int)
fmt.Printf("%p", ptr) // 输出类似 0xc000010200
函数参数传指针时,地址是怎么来的?
当你写 foo(&x),&x 在调用点即时求值,生成一个指向 x 栈上位置的地址,作为实参传入。这个动作本身不改变 x 的生命周期,但可能触发逃逸分析——如果编译器判断该指针可能逃出当前栈帧(比如被存进全局变量或返回),x 就会被分配到堆上。
- 不要假设
&x总是栈地址;逃逸后它就是堆地址 - 函数签名是
func f(p *int),你传&x是对的;但别在函数内对p做&p,那得到的是形参变量自身的地址,不是x的 - 接口类型变量的底层值地址不能直接取:
var i interface{} = 42; &i是*interface{},不是*int
真正容易被忽略的点是:可寻址性不是看“长得像不像变量”,而是编译器静态判定的内存稳定性。哪怕一个表达式逻辑上只读一次,只要它没绑定到具名变量,就大概率不可寻址。

















