
go 语言中数组是值类型,对指针解引用后赋值会生成副本,原数组不受影响;要真正修改原数组,需通过切片或直接解引用指针操作。
go 语言中数组是值类型,对指针解引用后赋值会生成副本,原数组不受影响;要真正修改原数组,需通过切片或直接解引用指针操作。
在 Go 中,理解数组([N]T)的行为是掌握内存模型的关键一环。与切片([]T)不同,数组是值类型——这意味着每次赋值、传参或解引用时,都会发生完整拷贝。这正是你示例中 x := *p 后修改 x[0] 却未影响原始数组 t 的根本原因。
我们来逐步拆解你的代码:
var t [5]int // t 是一个栈上分配的 [5]int 值,初始为 [0 0 0 0 0] p := &t // p 是指向 t 的指针,类型为 *[5]int x := *p // 关键!*p 返回 t 的一份完整副本,x 是独立的新数组 x[0] = 4 // 修改的是副本 x,而非 t 本身
注意:x := *p 并非“取地址并复用内存”,而是按值复制整个 5 个 int 的数据块(共 40 字节)。因此 x 拥有与 t 相同内容但完全独立的内存布局——printType 显示 x 的地址(%!p([5]int=...))看似模糊,实则是 reflect 对匿名临时值的打印惯例,不代表它与 t 共享地址。
✅ 正确修改原数组的两种方式:
立即学习“go语言免费学习笔记(深入)”;
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
方式 1:直接通过指针修改(推荐,语义清晰)
(*p)[0] = 4 // 解引用后直接索引赋值,作用于 t 本体 fmt.Println(t) // 输出: [4 0 0 0 0]
方式 2:转换为切片(利用切片的引用语义)
s := t[:] // s 是 []int,底层数据仍指向 t 的内存 s[0] = 4 fmt.Println(t) // 输出: [4 0 0 0 0]
⚠️ 注意事项:
- 不要误以为
x := *p创建了“别名”——Go 中没有 C 风格的引用变量,只有显式指针或切片; - 数组长度是类型的一部分(如
[3]int和[5]int是不同类型),无法隐式转换; - 若需频繁修改或传递大数组,优先考虑使用切片(
[]T)或指针(*[N]T),避免不必要的拷贝开销。
总结:Go 的数组设计强调值语义与内存安全。理解“赋值即拷贝”这一原则,能帮你规避大量隐蔽的逻辑错误。当需要共享状态时,请主动选择指针解引用或切片——而非依赖值类型的意外行为。

















