Go语言中所有参数均为值传递,但int等值类型复制全部内容,slice等引用类型仅复制header(含指针),故修改元素影响原变量;需改原始值必须传指针。

Go语言中没有传统意义上的“引用类型”,所有参数都是值传递,但不同数据类型的底层行为差异会让开发者产生“值类型”和“引用类型”的直观感受。关键在于:传递的是变量的副本,而这个副本的内容决定了它是“独立拷贝”还是“指向同一底层资源”的句柄。
哪些是常说的“值类型”
包括 int、float64、bool、string、struct、array 等。它们在赋值或传参时,整个数据内容被完整复制一份。
- 修改函数内参数,不会影响原始变量
- 注意:string 虽然不可变且底层含指针,但 Go 把它设计为值语义——每次赋值都复制 header(含指针和长度),但不复制底层数组;修改 string 实际是创建新 string,所以仍符合“值类型”行为直觉
哪些常被当作“引用类型”使用
包括 slice、map、channel、func、interface{}。它们本质是**头信息结构体(header)**,内部包含指针、长度、容量等字段,传参时只复制这个 header,而非底层数据。
- 修改 slice 元素(如
s[0] = 10)会影响原 slice,因为 header 中的指针指向同一底层数组 - 但对 slice 本身重新赋值(如
s = append(s, 1))可能扩容并生成新底层数组,此时原 slice 不受影响——因为只是改了副本 header 的指针,没动原来的 - map 和 channel 同理:增删元素会影响原变量;但用
make重新赋值给参数变量,不影响外部
指针类型:真正可控的“引用”方式
如果你需要在函数内修改原始变量的值(比如改变 int 变量内容、替换 struct 字段、或让 slice 指向新数组),必须显式传入指针。
立即学习“go语言免费学习笔记(深入)”;
- 声明形参为
*T,调用时传&x - 函数内通过
*p = ...修改原始内存位置的值 - 这是唯一能真正改变调用方变量内容的方式(除上述 header 类型的间接修改外)
一个对比示例
以下代码清晰展示差异:
func modifyInt(x int) { x = 999 }
func modifySlice(s []int) { s[0] = 999; s = append(s, 123) }
func modifySlicePtr(s *[]int) { *s = append(*s, 456) }
func main() {
a := 10
modifyInt(a)
fmt.Println(a) // 输出 10 —— 值类型,未变
s := []int{1, 2, 3}
modifySlice(s)
fmt.Println(s) // 输出 [999 2 3] —— 元素被改,但 append 没影响原 s
modifySlicePtr(&s)
fmt.Println(s) // 输出 [999 2 3 456] —— 通过指针成功追加
}










