golang中有指针的概念,有指针相对的就有取址,何时使用指针何时使用取址对于刚接触的同学总是有点傻傻分不清楚。
本篇主要介绍下指针和取址的概念和区别。

指针类似于变量和常量,在使用指针前你需要声明指针。指针声明格式如下:var name *Type
指针是指向一个值的内存地址,也就是说指针变量存放的是某个值或者对象的内存地址,即指针变量占用字节的大小与所指向的值的大小无关。当一个指针被定义后没有分配到任何变量时,它的默认值为nil。

指针变量存放的是一个值的内存地址,那如何获取一个变量的内存地址呢?那就需要使用取址符号&
将一个值的内存地址通过&赋值给了指针,那又应该如何通过指针来读取对应的值?需要使用符号*,需要与指针声明时的*进行区别。

看个代码感受下吧,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import (
"fmt"
)

func main() {
// 声明一个指针
var addr *string
var house = "Malibu Point 10880, 90265"
// 通过&将一个变量的内存地址赋值给指针
addr = &house
// 通过*获取指针中存储的地址对应的值
value := *addr
fmt.Printf("addr type: %T\n", addr)
// 输出指针存储的内存地址,也就是house的内存地址
fmt.Printf("address: %p\n", addr)
// 输出addr自身的内存地址
fmt.Printf("address: %p\n", &addr)
fmt.Printf("value type: %T\n", value)
fmt.Printf("value: %s\n", value)
}

// addr type: *string
// address: 0xc000010200
// address: 0xc00000e028
// value type: string
// value: Malibu Point 10880, 90265

代码先通过指针声明方式声明了一个addr指针,又通过取址符号&将house的地址赋值给addr,然后又通过*将指针addr存储内存地址对应的值赋值给普通变量value。由于指针addr变量存储的是变量house的内存地址,所以输出的addr的值为一个内存地址,而又因为指针addr本身也是一个变量,也需要一个内存块来存储自己,所以通过取址符号&得到的地址是指针变量的内存地址。

初看上面的代码,对其中的*&是不是傻傻分不清楚。&功能比较单一,就是取址,获取变量的内存地址。*两种含义,第一种是在指针声明时跟在变量的后面,紧跟变量类型,代表该变量是一个指针变量,如果使用%p来输出的话,它将是一个16进制数,第二种是紧跟指针变量,出现在指针变量的前面,代表获取该指针存储内存地址的值,一般是一个和指针类型一致的变量或者常量。在上面的demo代码中都有体现。

Tips
go中何时使用指针?

  • 当结构体较大的时候使用指针会更高效,可以避免内存拷贝
  • 如果要修改结构体内部的数据或状态必须使用指针
  • 如果方法的receiver是map、slice 、channel等引用类型不要使用指针
  • 如果该函数需要将传入变量的修改状态传递出去,可以使用指针