Go函数参数默认值传递,基本类型和无指针字段struct完全隔离;slice、map等仅复制header但共享底层数据,需显式拷贝避免意外修改。
Go语言中函数参数默认是值传递,这意味着传入函数的是原始数据的副本,函数内部对参数的修改不会影响原始变量。只要传入的是基本类型(如 int、string、struct)或其组合,且不涉及指针、切片、map、channel、interface 等引用类型底层结构,就能天然避免修改原数据。
Go没有真正的“引用传递”,所有参数都是值传递——但关键在于:这个“值”本身可能是地址(如 slice 的 header、map 的 hmap 指针)。所以真正安全的值类型参数,是指那些在内存中完整复制、不共享底层数据的类型。
int、float64、bool、string(注意:string 是只读字节序列+长度,底层数据不可变)、小 struct(不含指针或引用字段)*T)、带指针字段的 struct —— 复制的是地址,修改会反映到原数据若需传递一组相关字段并确保不可被意外修改,推荐定义普通 struct(无指针字段),它会在调用时整体拷贝:
type User struct {
ID int
Name string
Age int
}
func updateUser(u User) User { // u 是副本
u.Age++ // 只改副本
u.Name = "New" // string 赋值也是新副本(因 string 不可变)
return u
}
u1 := User{ID: 1, Name: "Alice", Age: 25}
u2 := updateUser(u1)
// u1 保持不变:{1 "Alice" 25}
// u2 是新值:{1 "New" 26}
即使你没用指针,slice 和 map 在函数内仍可修改底层数组或哈希表内容:
func badModify(s []int) {
if len(s) > 0 {
s[0] = 999 // 修改了原底层数组!
}
}
data := []int{1, 2, 3}
badModify(data)
// data 现在变成 [999, 2, 3] —— 原数据被改了
✅ 正确做法:如需只读或隔离,显式拷贝底层数组:
append([]T(nil), s...) 或 copy 到新 slicemaps.Clone)让调用者一眼明白是否会
影响原数据:
*T):明确表示“可能修改原数据”,符合 Go 社区惯例// Parse returns a copy; input is not modified
不复杂但容易忽略:只要传的是纯值类型(尤其小 struct 和 string),就无需额外防护;重点防范的是 slice/map 这类“头值+底层数组”混合体。