Go语言中向接口传递值与传递地址的区别是什么?
Go接口两种赋值方式的区别与底层原理
1. 两种赋值的核心差异
- 执行
abser = v时,接口变量存储的是Vertex实例的值拷贝,后续修改原始变量v的X/Y字段,接口内的副本不会同步变更,调用Abs()返回的是旧值的计算结果。 - 执行
abser = &v时,接口变量存储的是Vertex实例的指针拷贝,后续修改原始变量v的字段,接口内的指针指向同一块内存地址,调用Abs()会返回最新值的计算结果。
可以参考如下测试代码直观感受差异:
v := Vertex{1, 1} abser = v v.X = 100 fmt.Println(abser.Abs()) // 输出√(1+1)≈1.414,读取的是旧副本 abser = &v fmt.Println(abser.Abs()) // 输出√(100+1)≈10.05,读取的是修改后的原始变量
2. 指针类型赋值合法的原因
这是Go语言为方法调用提供的自动解引用语法糖机制:
当你为值类型Vertex实现了Abs()方法后,Go会自动为对应的指针类型*Vertex生成隐式的Abs()方法,调用时会自动解引用指针拿到底层的Vertex值,执行你定义的方法逻辑。因此*Vertex也天然满足Abser接口的实现要求,赋值合法。
注意该规则反向不成立:如果你是为
*Vertex指针类型实现了Abs()方法,那么只有指针类型满足接口要求,值类型Vertex不会自动生成对应方法,直接赋值abser = v会编译报错,避免修改副本产生不符合预期的行为。
3. 与接口底层实现的关联
Go的非空接口底层是名为iface的结构体,包含两个核心指针:
tab指针:指向itab表,存储接口类型信息、实际赋值的具体类型信息、以及具体类型对应接口方法的实现地址data指针:指向实际动态值的内存地址
两种赋值方式的底层差异直接体现在这两个指针上:- 赋值值类型
v时,itab中存储的具体类型为Vertex,data指针指向堆上分配的Vertex值副本的地址 - 赋值指针类型
&v时,itab中存储的具体类型为*Vertex,data指针指向原始变量v的内存地址
调用abser.Abs()时,Go会先从itab中找到对应类型的Abs()方法实现,再把data指针指向的内容作为接收者传入方法,因此两种场景都能正常执行逻辑。
内容的提问来源于stack exchange,提问作者mb2g17
相关产品推荐
相关产品推荐

