函数内修改slice元素生效而append不生效,因传参拷贝header且扩容后ptr指向新地址;map删除键生效但重赋值无效,因header指针拷贝;struct字段修改是否影响原变量取决于字段是否为引用类型。

函数里改 slice 元素生效,但 append 不生效?
因为 slice 传参时拷贝的是 header(ptr、len、cap),只要没扩容,ptr 指向的还是原底层数组。所以 s[0] = 100 能改到原数据;但 s = append(s, x) 可能触发扩容,新 ptr 指向新地址,这个赋值只作用于参数副本,调用方变量完全不变。
- 想让
append生效,必须接收返回值:s = f(s),或直接传*[]int - 判断是否扩容:看
len(s) == cap(s),等于时再append就大概率换底层数组 - 小切片(比如
[]int{1,2})反复append很容易扩容;大容量预分配的切片(make([]int, 0, 100))更稳定
传 map 进去删 key 会生效,但赋值给新 map 却无效
map 传参也是拷贝 header(含指向底层 hmap 的指针),所以 delete(m, "k") 或 m["x"] = 1 都操作同一块哈希表,调用方立刻可见;但 m = make(map[string]int) 只是把参数副本的 header 指针改了,原变量的 header 指针没动,所以毫无影响。
- 常见错误:函数开头没检查
if m == nil就直接m["k"] = v,panic:assignment to entry in nil map - 安全写法:要么文档注明“调用方需保证非 nil”,要么函数内主动初始化:
if m == nil { m = make(map[string]int) } - 如果真想让函数能把 map 置为
nil或换整个结构,必须传*map[string]int
struct 传值后,里面字段改不改得到原变量?
struct 永远是值类型,传参拷贝整个 struct 内存。但“改不改得到”取决于你改的是 struct 本身,还是它里面的引用字段:
- 改字段值(如
s.Name = "foo"):不影响原变量,因为 s 是副本 - 改引用字段的内容(如
s.Data[0] = 99或*s.Ptr = 42):会影响原变量,因为Data是 slice header,Ptr是指针,它们指向的底层内存没变 - 改引用字段本身(如
s.Data = append(s.Data, 1)或s.Ptr = &y):不影响原变量,只改了副本里的 header 或指针值
真正决定穿透性的,不是 struct 名字,而是字段内存布局 —— 有没有指针、slice、map。
string 和 array 看似像引用,其实很不一样
string 是只读 header(ptr + len),赋值拷贝这两个字段,但底层字节数组不可变,所以既高效又安全;[3]int 是纯值类型,传参会复制全部 3 个 int,哪怕很小也整块搬。
- 别对
string加指针来“避免拷贝”:没意义,还增加 GC 压力 -
[1024]int这种大数组传参开销明显,应优先用[]int或传*[1024]int - interface{} 装
int是值拷贝;装&x就共享内存 —— 这个细节在泛型函数里特别容易引发静默 bug
最可靠的判断方式不是背分类表,而是写两行代码实测:传进去,改内容,看原变量变没变。Go 从不骗人,只看你操作的是哪块内存。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











