Go-基础回顾-复合数据类型-Pointer

143 阅读4分钟

复合类型 - Pointer

概念

  • Go语言中的函数传参都是值拷贝,当我们想要修改某个变量的时候,我们可以创建一个指向该变量地址的指针变量。传递数据使用指针,而无须拷贝数据。类型指针不能进行偏移运算
  • 切片,由指向起始元素的原始指针、元素数量和容量组成。
  • Go语言中的指针操作非常简单,只需要记住两个符号:&(取地址)和*(根据地址取值)。

注:指针偏移和运算

Go中不存在该场景,OC/C/C++中均存在指针的偏移和运算的使用场景,一般用在数组类型中。

用Go来举个例子:

arr := [5]int{1,2,3,4,5}

arrPtr := &arr 

// arrPtr指向的是arr的首地址 即 arr[0]
// arrPtr + 1 = arr[1]
// arrPtr + 2 = arr[2]

优点:受益于这样的约束和拆分,Go语言的指针类型变量即拥有指针高效访问的特点,又不会发生指针偏移,从而避免了非法修改关键性数据的问题。同时,垃圾回收也比较容易对不会发生偏移的指针进行检索和回收。

提示:指针偏移带来的坑还挺有意思的,详见下方参考(不过是C语言的哦)

指针地址和指针类型

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

取值操作

ptr := &v // v 可以为任意类型

解释

v:代表被取地址的变量,类型为T ptr:用于接收地址的变量,ptr的类型就为*T,称做T的指针类型。*代表指针。

提示:变量、指针和地址三者的关系是,每个变量都拥有地址,指针的值就是地址。

代码说明

//指针
a := 10
// 对a 取地址
b := &a

// 打印a的值和a的地址
fmt.Printf("a:%d, ptr:%p \n", a, &a) // a:10, ptr:0xc0000b2008
// 打印b的地址和类型
fmt.Printf("b:%p, type:%T \n", b, b) // b:0xc0000b2008, type:*int
// 打印指针的地址
fmt.Println(&b) // 0xc0000ac018
// 打印指针指向的地址对应的值(有点绕)
fmt.Println(*b) // 10

// 利用指针修改原始变量的值
*b = 20
fmt.Printf("a:%d, ptr:%p \n", a, &a) // a:20, ptr:0xc0000b2008

image.png

解释说明

取地址操作符&和取值操作符*是一对互补操作符,&取出地址,*根据地址取出地址指向的值。

变量、指针地址、指针变量、取地址、取值的相互关系和特性如下:

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

使用指针的其他方法 - new 函数

Go语言还提供了另外一种方法来创建指针变量,格式如下:

func new(Type) *Type

用法

str := new(string)
*str = "Go语言教程"
fmt.Println(*str)

new() 函数可以创建一个对应类型的指针,创建过程会分配内存,被创建的指针指向默认值。

顺便提下make函数

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

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

make函数是无可替代的,我们在使用slice、map以及channel的时候,都需要使用make进行初始化,然后才可以对它们进行操作。

示例:

var b map[string]int
// b["test"] = 1 // 会报panic
b = make(map[string]int, 10)
b["测试"] = 100
fmt.Println(b)

new 和 make 的区别

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

空指针

  • 当一个指针被定义后没有分配到任何变量时,它的值为 nil
var p *int
fmt.Println(p)
if p != nil {
	fmt.Println("nil")
} else {
	fmt.Println("not nil")
}

参考