复合类型 - 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
解释说明
取地址操作符&和取值操作符
*是一对互补操作符,&取出地址,*根据地址取出地址指向的值。
变量、指针地址、指针变量、取地址、取值的相互关系和特性如下:
- 对变量进行取地址操作使用&操作符,可以获得这个变量的指针变量。
- 指针变量的值是指针地址。
- 对指针变量进行取值操作使用*操作符,可以获得指针变量指向的原变量的值。
使用指针的其他方法 - 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")
}