Golang中小值类型的接口实现与应用
Golang中小值类型的接口实现与应用 大家好,
我以为在我刚开始学习 Go 接口的工作原理时,在某个地方看到过:如果值足够小,能够放入 interface{} 的第二个 uintptr 中,那么值本身会被放入该位置,而不是创建一个指向该值的指针并将指针放入该位置。例如,我以为这段代码:
n := int32(1)
var i interface{} = n
data := *((*[2]uintptr)(unsafe.Pointer(&i)))
fmt.Println(data)
会输出:
[975264 1]
但我实际看到的是:
[975264 272826108]
其中第二个数字是指向实际值为 1 的 int32 的指针。
我现在正在谷歌搜索,想找到最初读到这个说法的地方,但还没找到。我的问题不是“为什么”,因为我观察到的行为与当接口中的值是无法放入 uintptr 的较大结构体时我所预期的一致。我的问题是:“我是不是完全搞错了,以为小值会被直接放入 interface{} 中?一直都是这样的吗?” 另外,“我这样测试是否正确?”
编辑 1:Russ Cox 在 2009 年 12 月 1 日关于 Go 接口的页面:https://research.swtch.com/interfaces
更多关于Golang中小值类型的接口实现与应用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
谢谢,雅各布,我会留意看看是否能找到原因。
更多关于Golang中小值类型的接口实现与应用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在 Go 语言中,接口值的内部表示确实包含一个用于存储数据的字段,但小值并不会直接内联存储到接口的第二个 uintptr 中。接口值由两部分组成:类型信息和数据指针。对于小值(如 int32),Go 会创建一个该值的副本,并将指向该副本的指针存储在接口的数据字段中。你观察到的行为是正确的,第二个数字确实是指向存储值 1 的 int32 副本的指针。
你的测试方法是正确的,它展示了接口的内部结构。以下是一个更详细的示例,演示了接口值的内部表示:
package main
import (
"fmt"
"unsafe"
)
func main() {
n := int32(1)
var i interface{} = n
data := *((*[2]uintptr)(unsafe.Pointer(&i)))
fmt.Printf("Interface data: %v\n", data)
// 提取类型指针和数据指针
typePtr := data[0]
dataPtr := data[1]
fmt.Printf("Type pointer: %v\n", typePtr)
fmt.Printf("Data pointer: %v\n", dataPtr)
// 通过数据指针访问实际值
value := *(*int32)(unsafe.Pointer(dataPtr))
fmt.Printf("Value through data pointer: %d\n", value)
}
输出示例(具体数字可能因运行环境而异):
Interface data: [975264 272826108]
Type pointer: 975264
Data pointer: 272826108
Value through data pointer: 1
这个行为在 Go 的早期版本中就已经存在,并且是设计的一部分。Russ Cox 在 2009 年的文章《Go 接口》中描述了接口的基本结构,但并未提到小值的内联优化。接口的数据字段始终存储指针,指向值的副本或原始值(如果是指针类型)。


