如何在Golang中理解值类型与引用类型区别_区分值传递和引用传递

15次阅读

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

如何在Golang中理解值类型与引用类型区别_区分值传递和引用传递

Go 里没有“引用类型”这个概念,只有值类型和指针

Go 官方文档从不使用“引用类型”一词。所有类型都是值类型——intStringStructslicemapchanfunc 全部按值传递。区别在于:有些类型(如 slice)的底层结构体里**包含指针字段**,导致它们在行为上“看起来像引用”,但本质仍是值拷贝。

常见误解:以为 mapslice 是“引用类型”,所以传参能自动修改原数据。其实只是它们的头部(header)被复制了,而 header 里存着指向底层数组/哈希表的指针。

  • slice 值包含三个字段:ptr(指向底层数组)、lencap;传参时这三个字段都被拷贝
  • map 值是一个 *hmap 指针;传参时拷贝的是该指针的值(即地址),不是整个哈希表
  • string 值包含 ptrlen;不可变,所以即使指针被拷贝,也无法通过它改原始内容

什么时候修改会影响原变量?看是否操作了指针所指的内存

关键判断依据:函数内对参数的修改,是否通过解引用(*)或下标([])写入了原内存地址。

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         // ✅ 解引用后写入原内存 }
  • slices[i] 赋值 → 影响原底层数组
  • slices = append(s, ...) → 可能分配新底层数组,只改 header 拷贝
  • mapm[k] = v → 总是影响原哈希表(因为 m 本身是 *hmap 拷贝)
  • struct 字段赋值 → 不影响原变量,除非你传的是 *struct

如何安全地让函数修改原始数据?明确选择传指针

不要依赖 slicemap 的“伪引用”行为来修改长度、容量或键集合。需要真正改变变量绑定关系时(比如让函数分配新 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] }
  • 想修改 slicelen/cap 或让它指向新底层数组 → 传 *[]T
  • 想修改 mapnil 状态(例如初始化一个空 map)→ 传 *map[K]V
  • chan 同理:传 *chan T 才能让函数把新 channel 赋给原变量
  • 普通 structArrayint 等,需传 *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 的拷贝 }
  • appendcopysort.Slice 等函数都不修改输入参数的 header,只可能修改其指向的数据
  • deletemap[key] = value 修改的是哈希表内容,所以生效
  • 如果函数内把 map 设为 nilm = nil),不影响调用方,因为只是改了指针拷贝

最常被忽略的一点:slicelencap 字段永远属于值拷贝,任何改变它们的操作(包括 append 导致扩容)都不会自动同步回原变量。要同步,必须显式返回或用指针传入。

text=ZqhQzanResources