因为Go默认值传递,参数是副本,修改不影响原变量;传指针则通过地址操作原始内存。值传递适合小数据只读,指针传递仅当需修改原始变量时使用。

为什么传指针才能修改函数外的变量?
因为 Go 默认是值传递:每次调用函数时,参数会被完整复制一份,函数里改的是副本,不影响原始变量。updateValue(x) 里的 x 和外面的 num 是两块内存,改 x 没用;但 updateValue(&num) 传的是地址,*x = 20 实际是在操作 num 所在的那块内存。
- 值传递:适合只读小数据(如
int、bool),语义清晰、线程安全 - 指针传递:仅当你**明确需要写入原始变量**时才用,不是“更高级”而是“有目的”
- 常见错觉:以为
func f(p *int) { p = &someLocal }能改变调用方的指针指向——不能,p本身仍是值传递,只能改它指向的值,不能改它自己存的地址
结构体传参不加 * 会悄悄拖慢程序
结构体哪怕只有两个字段,只要尺寸超过机器字长(通常 8 字节),Go 编译器就可能把它整个拷贝进栈;而一个 *Person 永远只占 8 字节(64 位系统)。对大结构体(比如含切片、数组或嵌套结构),值传递会触发大量内存分配和拷贝。
- 实测对比:
type Big struct{ data [10000]int },传值比传指针慢 3–5 倍,GC 压力明显升高 - 接口方法接收者也遵循同样逻辑:如果方法要修改结构体字段,接收者必须是
*T;否则编译报错cannot assign to s.Name (s is not addressable) - 切片、map、channel 本身已含指针(底层是结构体),传值不会复制底层数组,所以一般不用额外加
*
new() 和 & 都能得指针,但用途完全不同
new(T) 返回一个指向新分配的零值 T 的指针,适用于基本类型或简单结构体初始化;&v 是取已有变量地址,适用于你已有实例、只想共享或修改它。
-
p := new(int)→*int,值为0,等价于var tmp int; p := &tmp -
v := 42; p := &v→*int,值为42,且后续改*p就是改v - 别混用:
new([]int)得到的是*[]int(指向 nil 切片的指针),不是你想要的可 append 的切片;该用make([]int, 0)
并发场景下指针不是万能解药
多个 goroutine 共享同一指针指向的数据,不代表线程安全。没有同步机制时,ptr.Age++ 这种操作仍可能丢失更新(非原子)。
立即学习“go语言免费学习笔记(深入)”;
- 指针只是让多个 goroutine 访问同一块内存,但读写冲突需靠
sync.Mutex、atomic或 channel 协调 - 误以为“用了指针=自动线程安全”,结果跑一段时间出现随机数据错乱,这种 bug 极难复现
- 更推荐:优先用 channel 传递所有权,减少裸指针共享;实在要共享,把锁封装进结构体方法里









