不可寻址示例
对于不可变的值、临时结果、不安全的操作结果,均不可寻址。
package main
type Named interface {
Name() string
}
type Dog struct {
name string
}
func (dog *Dog) SetName(name string) {
dog.name = name
}
func (dog Dog) Name() string {
return dog.name
}
func main() {
// 1. 不可变的值不可寻址
const num = 123
//_ = &num // 常量不可寻址。
//_ = &(123) // 基本类型值的字面量不可寻址。
// 由于字符串值是不可变的,所有对它的索引或切片结果是不可寻址的
var str = "abc"
_ = str
//_ = &(str[0]) // 对字符串变量的索引结果值不可寻址。
//_ = &(str[0:2]) // 对字符串变量的切片结果值不可寻址。
// 2. 临时结果不可寻址
// 值字面量在还没有与任何变量绑定前是没有落脚点的
// 所以无法引用到它们,这样的值就是临时结果
//_ = &(123 + 456) // 算术操作的结果值不可寻址。
//_ = &([3]int{1, 2, 3}[0]) // 对数组字面量的索引结果值不可寻址。
//_ = &([3]int{1, 2, 3}[0:2]) // 对数组字面量的切片结果值不可寻址。
//_ = &([]int{1, 2, 3}[0:2]) // 对切片字面量的切片结果值不可寻址。
//_ = &(map[int]string{1: "a"}[0]) // 对字典字面量的索引结果值不可寻址。
// 对切片字面量的索引结果值却是可寻址的。
// 因为每个切片都会持有一个底层数组,底层数组的每个元素都有一个明确的内存地址
_ = &([]int{1, 2, 3}[0])
var chan1 = make(chan int, 1)
chan1 <- 1
//_ = &(<-chan1) // 接收表达式的结果值不可寻址。
// 3. 不安全的操作结果不可寻址
// 对字段类型的变量施加索引表达式,得到结果不属于临时结果,但不可寻址
// 因为字典中的每个键值元素对的存储位置都可能变化,这种变化外界无法感知
var map1 = map[int]string{1: "a", 2: "b", 3: "c"}
_ = map1
//_ = &(map1[2]) // 对字典变量的索引结果值不可寻址。
// 拿到指向一段代码的指针是不安全的
// 对函数或方法的调用结果值也是不可寻址的,因为它们属于临时结果
//_ = &(func(x, y int) int {
// return x + y
//}) // 字面量代表的函数不可寻址。
//_ = &(fmt.Sprintf) // 标识符代表的函数不可寻址。
//_ = &(fmt.Sprintln("abc")) // 对函数的调用结果值不可寻址。
dog := Dog{"little pig"}
_ = dog
//_ = &(dog.Name) // 标识符代表的函数不可寻址。
//_ = &(dog.Name()) // 对方法的调用结果值不可寻址。
//_ = &(Dog{"little pig"}.name) // 结构体字面量的字段不可寻址。
//_ = &(interface{}(dog)) // 类型转换表达式的结果值不可寻址。
dogI := interface{}(dog)
_ = dogI
//_ = &(dogI.(Named)) // 类型断言表达式的结果值不可寻址。
}
不可寻址的使用限制
package main
type Dog struct {
name string
}
func New(name string) Dog {
return Dog{name}
}
func (dog *Dog) SetName(name string) {
dog.name = name
}
func main() {
// 会被自动转换成(&dog).SetName("monster")
// 而 New 的调用结果是不可寻址的
New("little dog").SetName("monster") // 不能调用不可寻址的值方法
}
通过 unsafe.Pointer 操作可寻址的值
package main
import (
"fmt"
"unsafe"
)
type Dog struct {
name string
}
func (dog *Dog) SetName(name string) {
dog.name = name
}
func (dog Dog) Name() string {
return dog.name
}
func main() {
dog := Dog{"little dog"}
dogP := &dog
// unsafe.Pointer 是 *Dog 和 uinptr 之间的桥梁
// 一个指针值可以被转化为 unsafe.Pointer,但无法被直接转换为 uintptr
// dogPtr 是结构体在内存中的起始存储地址
dogPtr := uintptr(unsafe.Pointer(dogP))
// unsafe.Offsetof 获取两个值在内存中的起始存储地址之间的偏移量,单位为字节
// namePtr 是一个无符号整数,指向一个程序内部数据的内存地址
namePtr := dogPtr + unsafe.Offsetof(dogP.name)
// 再通过两次类型转换将 namePtr 转换为 *string
// 得到指向 dogP 的 name 字段值的指针
nameP := (*string)(unsafe.Pointer(namePtr))
fmt.Printf("nameP == &(dog.name)? %v\n", nameP == &(dogP.name))
}