&和不是语法糖,而是Go内存操作的唯二入口:&取变量地址生成指针,用于解引用或声明指针类型;for range中&v总返回同一地址,因v复用栈空间,正确做法是声明新变量再取址或直接取源元素地址。

& 和 * 不是语法糖,是内存操作的唯二入口。Go 指针没有“引用”类型,只有带类型的指针 *T;它不支持算术运算,也不允许跨类型强制转换——这不是限制,而是设计上对内存安全的硬约束。
为什么 &v 在 for range 循环里总返回同一个地址?
因为循环变量 v 是复用的栈空间:每次迭代只是覆写它的值,而非重新分配内存。所以 &v 永远指向同一块地址,最后所有指针都指向最后一次迭代的值。
- 典型错误:
for _, v := range items { ptrs = append(ptrs, &v) }→ 全是无效重复指针 - 正确做法:在循环内声明新变量,再取其地址:
for _, v := range items { vCopy := v; ptrs = append(ptrs, &vCopy) } - 更优解:直接从源切片/数组取地址,如
&items[i](前提是items是切片或数组,且生命周期足够长)
*T 传参时,函数内部改 p = &x 为什么不影响调用方?
指针本身是值类型,p 是 *T 类型的副本。你只能通过 *p 修改它指向的原始数据,但不能让调用方的指针变量指向新地址。
- 能生效的操作:
*p = newValue(修改原变量内容) - 无效操作:
p = &localVar或p = new(T)(只改副本,对外无影响) - 如果真需要重绑定指针,得传
**T,但极少见,通常说明设计可优化
什么时候该用 new(T),什么时候用 &T{}?
两者都返回 *T,但语义和零值处理不同。
-
new(T):只分配内存并置零,不调用任何构造逻辑,适用于所有类型,包括基本类型和结构体 -
&T{}:先按字面量初始化(字段显式赋值或默认零值),再取地址;支持字段选择性初始化,如&User{Name: "A"} - 结构体字段多、需部分初始化时,优先用
&T{...};仅需一个零值指针时,new(T)更直白
nil 指针解引用 panic 的本质是什么?
不是“空指针异常”,而是 Go 运行时主动拦截的非法内存访问:当 *p 执行时,若 p == nil,运行时会立即触发 panic: runtime error: invalid memory address or nil pointer dereference。
立即学习“go语言免费学习笔记(深入)”;
- 这不是编译期错误,而是在解引用那一刻才检查,所以必须在使用前显式判断:
if p != nil { ... } - 接口变量底层含指针时也可能隐含
nil,如var i io.Reader未赋值即为nil接口,此时i.Read(...)会 panic - 切片、映射、通道的零值也是
nil,但它们是引用类型头,和*T的nil行为不同,别混淆
for range 中变量复用和指针生命周期的耦合——它不报错,却让程序逻辑静默失效。比 panic 更危险的是这种“看起来正常,实则全错”的情况。


















