go函数传参均为值传递,包括slice、map等,修改需传指针;必须用t的场景:修改值类型字段、重置slice变量本身、共享内存避免拷贝;&和易误用,如对指针再取地址或解空指针。

为什么 Go 函数传参不能直接修改原始变量
Go 语言所有函数参数都是值传递,包括 struct、slice、map 这些看起来“像引用”的类型——它们底层仍是按值拷贝头信息(比如 slice 的指针、长度、容量三元组)。所以你在函数里对 slice 做 append,原变量可能不受影响;对 struct 字段赋值,原结构体也不会变。
想真正修改调用方的变量内容,必须传它的地址,也就是指针。这不是“可选优化”,而是唯一可靠路径。
什么时候必须用 *T 作参数类型
以下场景不传指针就无法达成目标:
- 函数需修改传入的
int、string、struct等值类型字段(例如:func increment(x *int) { *x++ }) - 函数要重置一个
slice变量本身(不只是元素),比如清空并回收底层数组:func clearSlice(s *[]int) { *s = nil } - 接收方需要和调用方共享同一块内存,避免大结构体拷贝开销(如含大量字段的
Config结构体)
注意:map 和 chan 本身是指针包装类型,传值即可增删元素,但若想让函数把整个 map 变量指向新 map(比如 m = make(map[string]int)),仍需 *map。
& 和 * 在调用时的典型误用
常见错误不是语法报错,而是语义错位:
- 对已是指针的变量再取地址:
var p *int; foo(&p)→ 实际传了**int,函数签名却写foo(x *int),编译失败 - 传值后在函数内对指针解引用前没判空:
func process(s *string) { fmt.Println(*s) },若调用process(nil)会 panic - 把局部变量地址传出去:
func bad() *int { x := 42; return &x }→ 返回栈上变量地址,行为未定义
安全做法:只对包级变量、切片底层数组、或明确生命周期长于函数的变量取地址;函数接收指针时,文档或注释中声明是否接受 nil。
对比:传 []T vs *[]T 的实际效果
这是最容易混淆的点。看两个例子:
func appendTo(s []int, v int) {
s = append(s, v) // 修改的是 s 的副本,不影响原 slice
}
func appendToPtr(s *[]int, v int) {
*s = append(*s, v) // 修改原 slice 变量指向
}
调用前者:data := []int{1}; appendTo(data, 2); fmt.Println(data) → 输出 [1];
调用后者:appendToPtr(&data, 2); fmt.Println(data) → 输出 [1 2]。
关键区别在于:前者只能改底层数组内容(如果容量够),后者能改 slice 头本身(比如扩容后指向新数组、或设为 nil)。
实际项目中,除非明确要替换整个 slice 变量,否则更常用的是传 []T + 返回新 slice(函数式风格),而不是强制用指针。指针在这里是“破坏性更新”的信号。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











