因为slice是含指针的描述符,值传递时指针仍指向同一底层数组;而int等类型值传递时整个值被拷贝。修改slice元素影响原变量,但修改int不影响;需改变量本身(如重分配slice头或赋新int值)时才需传* t。

为什么修改 slice 内容会影响原变量,但修改 int 不会?
因为 slice 的值本身是个“描述符”:它包含指向底层数组的指针、长度和容量。传参时复制的是这个描述符,而指针字段仍指向同一块内存。所以 s[0] = 100 实际改的是共享的底层数组。
-
int、string、struct等类型传参时,整个值被完整拷贝(哪怕string底层含指针,其 header 结构仍是值传递) -
slice、map、chan虽然也是值传递,但它们的 header 中含指针——复制 header ≠ 复制数据 - 常见错误:以为
append后原slice一定变长 → 其实可能扩容导致底层数组更换,原变量不受影响
什么时候必须传 *T 而不是 T?
当你需要在函数内修改变量本身的值(比如让一个 slice 指向新底层数组,或让一个 int 变成新数字),就必须传指针。
- 修改基本类型或结构体字段:必须用
*int、*User - 替换整个
slice(如s = append(s, x)后可能扩容):若想让调用方看到新头地址,得传*[]int - 传
map或chan本身不需要指针——因为它们的 header 已含指针;但若要让函数把nil map初始化成非空,就得传*map[K]V
make 和 new 对引用类型的影响差异
make 创建的是可直接使用的引用类型实例(slice、map、chan),new 只分配零值内存并返回指针,对引用类型几乎无用——除非你明确要一个指向 nil 值的指针。
-
new([]int)返回*[]int,其值是nil;解引用后仍是nil,不能直接append -
make([]int, 0)返回可用的空[]int,底层数组已分配(或延迟分配),可安全操作 - 混淆点:有人用
new(map[string]int),结果得到一个指向nil map的指针,if m == nil成立,但for range *mpanic
容易被忽略的性能陷阱:结构体里嵌套 slice 或 map 时的拷贝成本
结构体是值类型,但若它字段里含 slice 或 map,拷贝结构体只复制 header(小开销);可一旦字段是大数组或大 struct{ data [1,就真拷贝百万字节了。
立即学习“go语言免费学习笔记(深入)”;
- 传大结构体时,优先考虑传
*MyStruct,尤其当它不含引用类型字段时 - 含
slice字段的结构体,看似轻量,但若频繁赋值+扩容,底层数组可能被多次复制(append触发 realloc) - 调试技巧:用
unsafe.Sizeof(v)查结构体 header 大小,用reflect.ValueOf(v).Cap()看 slice 底层数组容量变化










