指针简介
变量的本质对一块内存空间的命名,我们可以通过引用变量名来使用这块内存空间存储的值,而指针则是用来指向这些变量值所在内存地址的值。
和 PHP、Python、Java 不同,Go 语言支持指针,如果一个变量是指针类型的,那么就可以用这个变量来存储指针类型的值。
C语言当中指针非常的灵活,学习成本非常高。
但是在Go语言当中,指针类型只有两种操作:&取址 和 * 取值
Go 语言之所以引入指针类型,主要基于两点考虑,一个是为程序员提供操作变量对应内存数据结构的能力;另一个是为了提高程序的性能(指针可以直接指向某个变量值的内存地址,可以极大节省内存空间,操作效率也更高)
指针的定义
var 指针变量名称 *类型
例子:
var ptr *int //定义int指针
fmt.Println(ptr) // 没有给指针变量赋值之前,指向空。
a := 100
ptr = &a //把变量a的地址复制给指针。
fmt.Println(ptr)
fmt.Println(*ptr) //取出指针指向地址的值
new
var p *int
*p = 10 //报错。
因为*int为指针类型(区别于以往学到的基本数据类型)基本数据类型声明未赋值时会默认零值,但是指针类型默认为nil,不会分配对应的存储空间。
之前我们都是通过先定义a := 100; ptr = &a 的方式使用指针,但是现在我想直接声明使用指针
这就要使用到new函数了,语法如下:
var p = new(int)
fmt.Println(p,*p) //new 完之后,p指向的地址给我们赋值为0
*p = 10
拓展
指针是一种特殊的变量,它存储了一个变量的内存地址。通过指针,我们可以通过指针直接修改变量的值,而不需要拷贝变量本身,这样可以提高程序的效率。
package main
import "fmt"
func main() {
var num1 int = 100
var num2 int = 200
changeVariable(&num1,&num2)
fmt.Println(num1,num2)
}
func changeVariable(p1,p2 *int) {
*p1,*p2 = *p2,*p1
}
- 传递变量的地址:指针可以作为函数参数,将变量的地址传递给函数,这样函数就可以直接修改变量的值。
- 动态分配内存:通过指针可以在运行时动态分配内存,这样程序就可以根据需要动态地创建和销毁变量。
- 函数返回值:指针可以作为函数的返回值,这样函数就可以返回一个指向变量的指针,而不是变量本身,这样可以避免变量拷贝和内存分配的开销
unsafe.Pointer
我们前面介绍的指针都是被声明为指定类型的,而 unsafe.Pointer 是特别定义的一种指针类型,它可以包含任意类型变量的地址(类似 C 语言中的 void 类型指针)。Go 官方文档对这个类型有如下四个描述:
- 任何类型的指针都可以被转化为
unsafe.Pointer; unsafe.Pointer可以被转化为任何类型的指针;uintptr可以被转化为unsafe.Pointer;unsafe.Pointer可以被转化为uintptr。
指针类型转化
unsafe.Pointer 可以在不同的指针类型之间做转化,从而可以表示任意可寻址的指针类型:
uintptr 是 Go 内置的可用于存储指针的整型,而整型是可以进行数学运算的!因此,将 unsafe.Pointer 转化为 uintptr 类型后,就可以让本不具备运算能力的指针具备了指针运算能力:
例z子:
arr := [3]int{1, 2, 3}
ap := &arr
sp := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(ap)) + unsafe.Sizeof(arr[0])))
*sp += 3
fmt.Println(arr)