12-指针

type Dog struct {   // 结构体类型,基本类型
    name string
}

func (dog *Dog) SetName(name string) {      // *Dog 指针类型
    dog.name = name
}

SetName方法的接收值就是Dog类型的指针值,通过指针值无缝访问基本值包含的任何字段、以及调用与之关联的任何方法。

指针是一个指向某个确切的内存地址的值,这个内存地址可以是任何数据或代码的起始地址,如,某个变量、字段、函数。

Go语言中的指针

Go语言内建的数据类型:uinptr类型是一个数值类型,最贴近传统意义上的指针。根据计算机架构的不同,它可以存储32或64为的无符号整数,可以代表任何指针的位(bit)模式,也就是原始的内存地址。

标准库unsafe包中的Pointer类型,代表了指针。unsafe.Pointer可以表示任何指向可寻址的值的指针,它也是指针值和uintptr值之间的桥梁。通过它可以在这两者之间转换。

可寻址的

不可寻址的值:

package main

type Named interface {
	// Name 用于获取名字。
	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]) // 对字符串变量的切片结果值不可寻址。
	str2 := str[0]
	_ = &str2 // 但这样的寻址就是合法的。(

	//_ = &(123 + 456) // 算术操作的结果值不可寻址。
	num2 := 456
	_ = num2
	//_ = &(num + num2) // 算术操作的结果值不可寻址。

	//_ = &([3]int{1, 2, 3}[0]) // 对数组字面量的索引结果值不可寻址。
	//_ = &([3]int{1, 2, 3}[0:2]) // 对数组字面量的切片结果值不可寻址。
	_ = &([]int{1, 2, 3}[0]) // 对切片字面量的索引结果值却是可寻址的。
	//_ = &([]int{1, 2, 3}[0:2]) // 对切片字面量的切片结果值不可寻址。
	//_ = &(map[int]string{1: "a"}[0]) // 对字典字面量的索引结果值不可寻址。

	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)) // 类型断言表达式的结果值不可寻址。
	named := dogI.(Named)
	_ = named
	//_ = &(named.(Dog)) // 类型断言表达式的结果值不可寻址。

	var chan1 = make(chan int, 1)
	chan1 <- 1
	//_ = &(<-chan1) // 接收表达式的结果值不可寻址。

}

不可寻址的总结:

  • 不可变的:常量、基本类型字面量、字符串、函数以及方法的字面量
  • 临时结果:算术操作的结果,对数组值、切片值、字典值字面量施加的表达式的求值结果;如果把一个临时结果赋给一个变量,那它就是可寻址的
  • 不安全: 字典类型的变量施加索引值得到的结果不属于临时结果,但是字典中键值对的地址是会变化的,获取指针不安全

对函数或方法的调用属于临时结果、拿到一段代码的指针是不安全的

表达式整理:

  • 索引表达式
  • 切片表示式
  • 选择表达式
  • 调用表达式
  • 类型转换表达式
  • 类型断言表达式
  • 向通道发送或者从通道接收的接收表达式

对数组或切片类型的变量的索引或切片的结果值不是临时结果

不可寻址的值的使用限制

无法使用取地址操作符(&)获取指针;对不可寻址值取地址,编译器会报错

在上述例子的基础上编写新函数:

func New(name string) Dog {
    return Dog{name}
}

func main() {
    New("little pig").SetName("monster")    // 编译报错,不能取得New("little pig")的地址
}

例外情况:

  1. ++--的左边是一个表达式(表达式的结果值必须是可寻址的),就可以组成一个自增或自减语句,所以针对值字面量的表达式几乎都无法在这里使用。字典字面量和索引表达式可以用
  2. 赋值语句中,赋值操作左边的表达式的结果必须是可寻址的,字典的索引结果可用
  3. 带range子句的for循环中,range关键字左边的表达式的结果值必须是可寻址的,字典的索引结果可用
上次修改: 25 November 2019