09- Go语言基础之指针

区别于C/C++中的指针, Go 语言中的指针不能进行偏移量和运算. 是安全指针.

要搞明白Go语言中的指针需要知道三个概念:

  • 指针地址
  • 指针类型
  • 指针取值

Go 语言中的指针

任何程序数据载入内存后, 在内存都有他们的地址, 这就是指针. 而为了保存一个数据在内存中的地址, 我们就需要指针变量.

Go语言中的指针不能进行偏移和运算,因此Go语言中的指针操作非常简单,我们只需要记住两个符号:&(取地址)和*(根据地址取值)

指针地址和指针类型

每个变量在运行时都拥有一个地址, 这个地址代表变量在内存中的位置. Go 语言中使用&字符放在变量前面对变量进行’取地址’ 操作. Go 语言中的值类型(int, float, bool, string, array, struct) 都有对应的指针类型. 如: *int, *int64

取弄了指针的语法如下:

  1. prt := &v

其中:

  • v: 表示被取地址的变量, 类型为T
  • prt: 用来接收地址的变量. prt 的类型就位 T, 称作T的指针类型. 代表指针.

举例:

  1. func main() {
  2. a := 10
  3. b := &a
  4. fmt.Printf("a:%d ptr:%p\n", a, &a) // a:10 ptr:0xc00001a078
  5. fmt.Printf("b:%p type:%T\n", b, b) // b:0xc00001a078 type:*int
  6. fmt.Println(&b) // 0xc00000e018
  7. }

图示:

09- Go语言基础之指针 - 图1

指针取值

在对普通变量使用& 操作取地址后, 会获得变量的指针, 然后我们可以使用* 操作, 对指针取值.

  1. func main() {
  2. //指针取值
  3. a := 10
  4. b := &a // 取变量a的地址,将指针保存到b中
  5. fmt.Printf("type of b:%T\n", b)
  6. c := *b // 指针取值(根据指针去内存取值)
  7. fmt.Printf("type of c:%T\n", c)
  8. fmt.Printf("value of c:%v\n", c)
  9. }

输出:

  1. type of b:*int
  2. type of c:int
  3. value of c:10

总结:

取地址操作& 和取值操作* 是一对互补操作符,

变量, 指针地址, 指针变量, 取地址, 取值的相互关系特征:

  • 对变量进行取地址&操作, 可以获得这个变量的指针变量.
  • 指针变量的值是指针地址.
  • 对指针变量进行取值*操作, 可以获得指针变量指向的原变量的值.

指针传值示例:

  1. func modify1(x int) {
  2. x = 100
  3. }
  4. func modify2(x *int) {
  5. *x = 100
  6. }
  7. func main() {
  8. a := 10
  9. modify1(a)
  10. fmt.Println(a) // 10
  11. modify2(&a)
  12. fmt.Println(a) // 100
  13. }

new 和 make

例子:

  1. func main() {
  2. var a *int
  3. *a = 100
  4. fmt.Println(*a)
  5. var b map[string]int
  6. b["das"] = 100
  7. fmt.Println(b)
  8. }

上述例子 会引发 panic

  1. panic: runtime error: invalid memory address or nil pointer dereference
  2. [signal 0xc0000005 code=0x1 addr=0x0 pc=0x27514a]
  3. goroutine 1 [running]:
  4. main.main()
  5. C:/Users/zhang/go/src/Lear/newAndMake.go:7 +0x2a

在Go语言中对于引用类型的变量,我们在使用的时候不仅要声明它,还要为它分配内存空间,否则我们的值就没办法存储。而对于值类型的声明不需要分配内存空间,是因为它们在声明的时候已经默认分配好了内存空间。要分配内存,就引出来今天的new和make。 Go语言中new和make是内建的两个函数,主要用来分配内存。

new

new 是一个内置函数, 它的函数签名为:

  1. func new(Type) *Type

其中

  • Type 表示类型, new 函数只接受一个参数, 这个参数是一个类型.
  • *Type表示类型指针, new函数返回一个指向该类型内存地址的指针.

new 函数不太常用, 使用new函数得到的是一个类型的指针. 并且该指针类对应的值为该类型的零值,

  1. func main() {
  2. a := new(int)
  3. fmt.Printf("%T", *a) // 0
  4. }

指针作为引用类型, 需要出示化后才会拥有内存空间, 才可以给它赋值, 应该按照如下方式使用内置的new韩硕对a 进项初始化, 然后赋值.

  1. func main() {
  2. var a *int
  3. a = new(int)
  4. *a = 10
  5. fmt.Println(*a)
  6. }

make

make 也是用于内存分配的, 区别于new, 它只用于slice,map 以及chan的内存创建, 而且它返回的类型就是这三个类型本身, 而不是他们的指针类型, 因为这三种类型就是引用类型, 所以就没有必要返回他们的指针了.

  1. func make(t Type, size ...IntegerType) Type

make 韩硕是无可替代的, 我们在使用slice,map, 以及channel的时候, 都需要使用make初始化, 然后才可以对他们进行操作.

例如:

  1. func main() {
  2. var b map[string]int
  3. b = make(map[string]int, 10)
  4. b["沙河娜扎"] = 100
  5. fmt.Println(b)
  6. }

new 与 make的区别

  • 二者都是用来做内存分配的.
  • make只用于slice, map 以及channel的初始化, 返回的还是这三个引用类型本身.
  • new 用于类型的内存分配, 并且内存对应的值为类型的零值, 返回的是指向类型的指针