

新闻资讯
技术学院Go中只有值传递,&x取地址得T类型值,p解引用读取指向的值;传指针本质是传地址值副本,修改指针本身不影响原变量。
& 和 * 到底在干啥Go 中没有“引用传递”,只有值传递;但你可以传一个指针类型的值,它本身存的是地址。所以关键不是“传什么”,而是“传进去的东西是什么类型”。&x 取出变量 x 的内存地址,得到一个 *T 类型的值;*p 是解引用,从地址 p 读出它指向的 T 类型的值。
常见错误是以为写了 func f(p *int) { p = &y } 就能改外面的指针变量本身——其实不能,因为 p 是 *int 的副本,你只是把它内部存的地址换掉了,原变量没变。
&x 的结果是一个新生成的指针值(地址),类型为 *T
*p 操作需要 p != nil,否则 panic: invalid memory address or nil
pointer dereference
&S{...} 得到的是指向新分配结构体的指针,不是对已有变量取地址*T 还是 T?看这三点决定是否用指针传参,不看“要不要修改”,而看三件事:数据大小、是否真要修改原值、API 一致性。
int、bool、[3]int)传值开销小,优先传值;大结构体(比如含 []byte 或几十字段)传指针避免拷贝json.Unmarshal(&v),那就得传 *T;否则传值更安全、更易测试(*T).Method(),那其他函数也建议接收 *T,避免混用造成理解成本type User struct {
Name string
Age int
}
func updateUser(u *User) { // 明确表示会改 u 指向的值
u.Name = "Alice"
}
func copyUser(u User) User { // 不动原值,返回新副本
u.Name = "Bob"
return u
}
它们底层都是结构体(header),包含指针字段。例如 slice 实际是 struct{ ptr *elem, len, cap }。传 slice 时,这个 header 被完整拷贝,但其中的 ptr 字段仍指向同一块底层数组——所以改元素会反映到原 slice,但做 s = append(s, x) 可能导致扩容,新 header 的 ptr 就指向别处了,原 slice 不受影响。
map 和 channel 同理:header 拷贝,内部指针共享;但 make(map[int]int) 返回的是 header 值,不是指针*[]T,即指向切片 header 的指针func badAppend(s []int) { s = append(s, 99) } —— 外面的 slice 完全不受影响func goodAppend(s *[]int) {
*s = append(*s, 99)
}
nums := []int{1, 2}
goodAppend(&nums) // nums 现在是 [1 2 99]
new(T) 或 &T{}
new(T) 返回 *T,且把内存清零;&T{} 也返回 *T,但按字段顺序初始化(未写的字段仍为零值)。二者都不常用,多数时候直接写 &User{Name: "X"} 更清晰。
new(T) 唯一不可替代场景:你需要一个零值指针,但 T 没有字面量语法(比如接口、函数类型)——但这种情况极少&T{} 常用于初始化结构体,尤其字段多或含嵌套时;注意 &struct{X int}{1} 合法,但 struct{X int}{1} 是值,不是地址var x *int; x = new(int) 不如 x := new(int) 直观,更不如 var x int; px := &x 明确*T 赋给 interface{},传进去的仍是那个指针值的副本——但它所指的地址没变,所以通过 interface{} 调用方法仍能修改原值。这点让很多人误以为 interface{} 是引用传递。