Golang中[]interface{}和interface{}转换相同变量c时为何行为不同?

Golang中[]interface{}和interface{}转换相同变量c时为何行为不同?

类型转换:为什么 []interface{} 和 interface{} 在转换同一个变量 c 时表现不同?

你做了什么?

在这个应用中,你可以看到 uint8 类型的 c 将值赋给 i 和 i2,并进行直接转换,这意味着你可以看到 uint8 类型的 c 被转换为 interface{},这始终是一个一致的地址,这在这里是可以理解的。

然后是第二个例子

这个例子再次使用了 uint8 类型的 c 将值赋给 i,此外,unsafe 包被转换为指针 p,用于修改 i 的值。在 fmt.Println© 中测试后,p 修改的值实际上影响了 c。fmt.Println© 的输出是 30,但通过 println 可以看到 c 的值是 20。这是因为 c 被转换为 []interface{} 类型传入 fmt.Println,并且当 uint8 进行 interface{} 转换时,它与 uint8 是相同的内存地址。

然后是第三个例子

package main

import (
	"fmt"
	"unsafe"
)

type ef struct {
	_type unsafe.Pointer
	data  unsafe.Pointer
}

func main() {
	var c uint32 = 20
	var i interface{} = c
	p := unsafe.Pointer(&i)
	ptr := (*ef)(p)
	p2 := (*int64)(ptr.data)
	*p2 = 30
	println(c)

	fmt.Println(c)

}

将 c 改为 uint32 类型(不是 uint8 类型),你可以看到修改 p 的值不会影响 c,这当然是因为 c 在每次通过 interface{} 转换时地址解释不一致,因为 golang 是值拷贝的。

最后,当我们在 fmt.Println© 中添加任何值时,例如 fmt.Println(p, c),我们发现即使是 uint32 类型的 c 变量也被 p 修改,在第四个例子中打印出 30。

package main

import (
	"fmt"
	"unsafe"
)

type ef struct {
	_type unsafe.Pointer
	data  unsafe.Pointer
}

func main() {
	var c uint32 = 20
	var i interface{} = c
	p := unsafe.Pointer(&i)
	ptr := (*ef)(p)
	p2 := (*int64)(ptr.data)
	*p2 = 30
	println(c)

	fmt.Println(p, c)

}

你期望看到什么?

在第四种情况下,当将 c 和其他参数转换为 interface{} 时,没有使用相同的内存地址。并且 fmt.Println© 的输出应该是 20。

你实际看到了什么?

我对 []interface{} 进行转换的规则感到困惑,为什么在转换 uint8 类型时会出现相同的地址,而其他类型则不会。

为什么 []interface{} 和 interface{} 在转换同一个变量 c 时表现不同?

非常感谢可能的答案。并将测试代码粘贴在下面:

package main

import (
	"fmt"
	"unsafe"
)

type ef struct {
	_type unsafe.Pointer
	data  unsafe.Pointer
}

func main() {
	var c uint32 = 20
	var i interface{} = c
	p := unsafe.Pointer(&i)
	ptr := (*ef)(p)
	p2 := (*int64)(ptr.data)
	*p2 = 30
	println(c) // output: 20

	fmt.Println(p, c) // output: 0xc000014270 30
}

更多关于Golang中[]interface{}和interface{}转换相同变量c时为何行为不同?的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang中[]interface{}和interface{}转换相同变量c时为何行为不同?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


这是一个关于Go语言接口底层表示和内存布局的问题。关键区别在于值的大小和内存对齐方式。

package main

import (
	"fmt"
	"unsafe"
)

// 接口的底层表示(简化版)
type iface struct {
	tab  unsafe.Pointer
	data unsafe.Pointer
}

func main() {
	// 情况1: uint8(1字节)
	var c1 uint8 = 20
	var i1 interface{} = c1
	println("uint8 - 直接存储在接口中")
	println("c1地址:", unsafe.Pointer(&c1))
	println("i1.data:", (*iface)(unsafe.Pointer(&i1)).data)
	
	// 情况2: uint32(4字节)
	var c2 uint32 = 20
	var i2 interface{} = c2
	println("\nuint32 - 需要额外分配内存")
	println("c2地址:", unsafe.Pointer(&c2))
	println("i2.data:", (*iface)(unsafe.Pointer(&i2)).data)
	
	// 情况3: 验证修改
	var i3 interface{} = c2
	p := unsafe.Pointer(&i3)
	ptr := (*iface)(p)
	
	// 获取实际存储的值
	if uintptr(ptr.data) == uintptr(unsafe.Pointer(&c2)) {
		println("直接引用原变量")
	} else {
		println("复制到新内存位置")
		// 修改复制的值
		*(*uint32)(ptr.data) = 30
		println("c2:", c2) // 仍然是20
		println("i3:", i3) // 变为30
	}
}

对于小值(通常≤指针大小),Go会直接将其存储在接口的data字段中。对于大值,会分配新内存并复制。fmt.Println接收...interface{},每个参数单独转换,可能产生不同的内存行为。

回到顶部