Go中无引用类型,所有类型均按值传递;slice、map等“似引用”行为源于其底层含指针字段,修改底层数组或哈希表内容才影响原变量,改header(如len/cap)则不会。

Go 里没有“引用类型”这个概念,只有值类型和指针
Go 官方文档从不使用“引用类型”一词。所有类型都是值类型——int、string、struct、slice、map、chan、func 全部按值传递。区别在于:有些类型(如 slice)的底层结构体里**包含指针字段**,导致它们在行为上“看起来像引用”,但本质仍是值拷贝。
常见误解:以为 map 或 slice 是“引用类型”,所以传参能自动修改原数据。其实只是它们的头部(header)被复制了,而 header 里存着指向底层数组/哈希表的指针。
-
slice值包含三个字段:ptr(指向底层数组)、len、cap;传参时这三个字段都被拷贝 -
map值是一个*hmap指针;传参时拷贝的是该指针的值(即地址),不是整个哈希表 -
string值包含ptr和len;不可变,所以即使指针被拷贝,也无法通过它改原始内容
什么时候修改会影响原变量?看是否操作了指针所指的内存
关键判断依据:函数内对参数的修改,是否通过解引用(*)或下标([])写入了原内存地址。
func modifySlice(s []int) {
s[0] = 999 // ✅ 修改底层数组,原 slice 可见
s = append(s, 1) // ❌ 只修改了 s 的 header 拷贝,不影响调用方
}
func modifyMap(m map[string]int) {
m["key"] = 999 // ✅ 写入 *hmap 指向的哈希表,原 map 可见
}
func modifyStruct(v struct{ x int }) {
v.x = 999 // ❌ 只改了拷贝的 struct,原变量不变
}
func modifyStructPtr(v *struct{ x int }) {
v.x = 999 // ✅ 解引用后写入原内存
}
- 对
slice用s[i]赋值 → 影响原底层数组 - 对
slice用s = append(s, ...)→ 可能分配新底层数组,只改 header 拷贝 - 对
map用m[k] = v→ 总是影响原哈希表(因为m本身是*hmap拷贝) - 对
struct字段赋值 → 不影响原变量,除非你传的是*struct
如何安全地让函数修改原始数据?明确选择传指针
不要依赖 slice 或 map 的“伪引用”行为来修改长度、容量或键集合。需要真正改变变量绑定关系时(比如让函数分配新 slice 并返回给调用方),必须传指针。
立即学习“go语言免费学习笔记(深入)”;
func reallocSlice(s *[]int) {
*s = append(*s, 1, 2, 3) // ✅ 解引用后赋值,调用方看到新 slice
}
func main() {
s := []int{0}
reallocSlice(&s) // 必须取地址
fmt.Println(s) // [0 1 2 3]
}
- 想修改
slice的len/cap或让它指向新底层数组 → 传*[]T - 想修改
map的nil状态(例如初始化一个空 map)→ 传*map[K]V -
chan同理:传*chan T才能让函数把新 channel 赋给原变量 - 普通
struct、array、int等,需传*T才能修改原值
容易踩的坑:误以为 append 或 map delete 会反映到调用方
append 返回新 slice,但不会自动更新原变量;delete 会生效,是因为它操作的是 map 指针所指的哈希表,而非 map header 本身。
func badAppend(s []int) {
s = append(s, 999) // s 现在指向新底层数组,但只是局部变量
}
func goodAppend() []int {
return append(s, 999) // 显式返回,由调用方重新赋值
}
func deleteFromMap(m map[string]int) {
delete(m, "key") // ✅ 删除成功,因为 m 是 *hmap 的拷贝
}
-
append、copy、sort.Slice等函数都不修改输入参数的 header,只可能修改其指向的数据 -
delete和map[key] = value修改的是哈希表内容,所以生效 - 如果函数内把
map设为nil(m = nil),不影响调用方,因为只是改了指针拷贝
最常被忽略的一点:slice 的 len 和 cap 字段永远属于值拷贝,任何改变它们的操作(包括 append 导致扩容)都不会自动同步回原变量。要同步,必须显式返回或用指针传入。










