go---关于指针的有限操作

不可寻址示例

对于不可变的值、临时结果、不安全的操作结果,均不可寻址。

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))
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值