Go中值类型赋值是内存拷贝而非引用共享,包括基础类型、struct、array等;struct若含指针、slice、map等字段则仅拷贝头结构,底层数据仍共享,需手动深拷贝或显式传指针避免意外修改。

在 Go 中,值类型赋值本质是内存拷贝,不是引用共享。理解这一点,就能避开绝大多数“改了A,B也变了”或“改了A,B却没变”的困惑。
哪些是值类型?
Go 的值类型包括:
- 基础类型:int、float64、bool、string(注意:string 是只读的值类型,底层有指针但语义不可变)
- 复合值类型:struct、array
- 其他:complex64/128、uintptr、interface{}(空接口本身是值类型,但可装引用类型)
关键判断标准:声明时不用 *,且变量直接持有数据本身 —— 赋值时就复制整块数据。
struct 赋值 = 深拷贝(浅层)
struct 是典型值类型。只要它的所有字段都是值类型,赋值就是完整拷贝:
立即学习“go语言免费学习笔记(深入)”;
示例:type User struct { Name string; Age int }
u1 := User{Name: "Alice", Age: 30}
u2 := u1 // ✅ 完全独立副本
u2.Name = "Bob"
fmt.Println(u1.Name, u2.Name) // Alice Bob
但如果 struct 含指针、slice、map、channel 或 func 字段,这些字段本身是引用类型 —— 它们存储的是地址,赋值时地址被复制,但指向的底层数据仍共享:
陷阱示例:type Config struct { Data map[string]int }
c1 := Config{Data: map[string]int{"x": 1}}
c2 := c1 // ❌ map header 被拷贝,但底层数组共用
c2.Data["x"] = 99
fmt.Println(c1.Data["x"]) // 输出 99 —— 意外修改!
slice、map、channel 是引用类型头,但本身是值类型
这是最容易混淆的点:slice/map/channel 变量本身是值类型(可赋值、传参不加 *),但它们内部包含指向底层数据的指针(如 slice 的 pointer/len/cap)。所以:
- 赋值操作拷贝的是这个“头结构”,不是底层数组或哈希表
- 多个变量可能共享同一底层数组(slice)或哈希桶(map)
—— 对 slice 头修改(如重切片、追加扩容前)不影响原头;
—— 对底层元素修改(如 s[0] = x)会影响所有共享该数组的 slice。
如何避免意外修改?
明确意图,按需选择:
- 需要隔离修改 → 手动深拷贝:对 map 用循环复制,对嵌套 struct 用 encoding/gob 或第三方库(如 copier)
- 想共享状态 → 显式传指针:
func update(u *User),并用&u1调用 - 函数参数中不确定是否修改 → 默认按值传,除非文档/签名明确要求指针
- 返回局部 struct?放心:返回的是拷贝,不会暴露栈内存
记住一句口诀:值类型赋值拷贝“自己”,不拷贝“自己指向的东西”。看清字段类型,就看清了共享边界。










