Go中所有参数都是值传递,传struct时修改副本不影响原变量;需用指针接收者才能修改原值;切片/map/channel是含指针的值类型;是否用指针取决于是否需修改、大小及接口实现。
为什么传 struct 时函数里改了字段,外面却没变?
因为 Go 所有参数都是值传递——
传进去的是整个
的副本,函数内修改的只是副本字段,原变量毫发无伤。
想让外部看到修改,必须用指针:
,然后在函数里写
哪怕结构体只有两个
字段,只要方法需要改状态(比如
),就该用指针接收者
常见错误现象:
调用后
还是空字符串——十有八九接收者写成了
,而不是
切片、map、channel 为啥“像引用”但又不是引用类型?
它们是「含指针的值类型」:变量本身是值(比如
结构体),但内部字段存着底层数组/哈希表的地址。所以传参时虽拷贝 header,但底层数据仍共享。
可能扩容并返回新 slice,原变量不变;而
直接生效,因 map header 里的指针指向同一 hash 表
误以为「map 是引用类型」会导致误判:把 map 当参数传进函数,函数里清空它(
),调用方确实看到空了;但若函数里做了
,这只会改副本 header,不影响外层
安全做法:需要保证行为可预测时,显式传
或
(虽然极少这么做)
nil 指针解引用 panic 和空接口赋值失败,根源都是同一套规则
Go 中一切皆值传递,但「值」的内容决定你能干什么:
是
类型的零值,代表「地址为空」;而空接口
存的是(类型, 数据)对,当值为
时,类型信息可能丢失。
→ 直接 panic:不能解引用
指针
→ 输出
!因为
存的是 (
,
),类型非空,整体不等于
判断指针是否有效,永远先做
;判断接口是否为 nil,要拆成
(或更稳妥地用类型断言)
什么时候该用指针,什么时候死守值类型?
核心就看三件事:要不要改原值、对象大不大、接不接口。
Python3.6标准库参考手册(PDF版)
Python3.6标准库参考手册(PDF版)
下载
立即学习
“
go语言免费学习笔记(深入)
”;
要改原值 → 必须用指针(
参数或接收者)
结构体字段总大小 > 16
字节
(如含
或多个
)→ 优先指针,避免复制开销
实现了某个接口?注意方法集:如果只有
,那
值本身无法赋给该接口变量,必须传
小而只读的数据(
、
、轻量
)→ 传值更清晰,无 nil 风险,GC 压力小
最容易被忽略的一点:即使结构体很小,只要它和指针接收者的方法混用(比如
标准库
),就必须统一用指针——否则调用
时传值会触发拷贝,锁的不是同一个实例。
func f(s MyStruct)MyStructfunc f(s *MyStruct)s.Field = "new"intuser.IncreaseLoginCount()user.SetName("Alice")user.Namefunc (u User) SetName(n string)func (u *User) SetName(n string)sliceHeaderappendmap["k"] = vclear(m)m = make(map[string]int)*map*[]Tnil*Tinterface{}nilvar p *string; fmt.Println(*p)nilvar s *string; var i interface{} = s; fmt.Println(i == nil)falsei*stringnilnilif p != nilif i == nil || reflect.ValueOf(i).IsNil()*T[32]bytestringfunc (t *T) Foo()T{}&T{}inttime.Timestruct{ ID int; Name string }sync.MutexLock()