golang基础(18.指针的基本概念和使用)
指针概述
在go中我们可以通过变量来定义操作我们的物理存储空间,其本质是一块内存空间的定义。而指针的定义是指指向存储这些变量值的内存地址。
package main
import "fmt"
func main() {
var a = 100
var ptr *int
ptr = &a
fmt.Println(a) // 变量值
fmt.Println(ptr) // 变量值存储地址
}
上述代码定义了一个整形变量a值是一百,然后定义了一个整形指针。通过&符号,将变量a的变量值地址赋值给了指针ptr。我们可以通过 *ptr 获取指针指向内存地址存储的变量值(我们通常将这种引用称作「间接引用」),ptr 本身是一个内存地址值(通过 &a 可以获取变量 a 所在的内存地址)<br />go语言引入指针类型,主要基于两点考虑。一个是为程序员提供操作变量对应内存数据结构的能力,一个是为了提供程序性能。(指针可以值直接传递某个变量的内存地址,可以在传递过程当中产生的值拷贝)<br />指针在go中有两个使用场景
- 类型指针
- 数组切片
作为类型指针时,允许对这个指针类型的数据直接进行修改指向其他内存地址,传递数据时如果使用指针则无须拷贝数据从而节省内存空间,此外和 C 语言中的指针不同,Go 语言中的类型指针不能进行偏移和运算,因此更为安全。数组切片,由指向起始元素的原始指针、元素数量和容量组成,所以切片与数组不同,是引用类型,而非值类型。
指针的基本使用
指针类型的声明和初始化
指针变量传值时之所以可以节省空间,因为指针指向的内存地址大小是固定的,在32位机器上占4个字节,在64位上占8个字节,与指向内存存储的值无关。
var ptr *int
fmt.Println(ptr)
a := 100
ptr = &a
fmt.Println(ptr)
fmt.Println(*ptr)
当指针被声明后,没有指向任何变量内存地址时,它的零值是 nil,然后我们可以通过在给定变量前加上取地址符 & 获取变量对应的内存地址将其赋值给声明的指针类型,这样,就是对指针的初始化了,然后我们可以通过在指针类型前加上间接引用符 * 获取指针指向内存空间存储的变量值。当然,我们也可以通过 := 对指针进行初始化:
a := 100
ptr := &a
fmt.Printf("%p
", ptr)
fmt.Printf("%d
", *ptr)
底层会自动判断指针的类型,在格式化输出时,可以通过 %p 来标识指针类型。
通过指针传值
我们再来看一个通过指针传值的示例,通过指针传值就类似于 PHP 中通过引用传值,这样做的好处是节省内存空间,此外还可以在调用函数中实现对变量值的修改,因为直接修改的是指针指向内存地址上存储的变量值,而不是值拷贝。
package main
import "fmt"
func swap(a, b int) {
a, b = b, a
fmt.Println(a, b)
}
func swapPtr(a, b *int) {
*a, *b = *b, *a
fmt.Println(*a, *b)
}
func main() {
a := 1
b := 2
swap(a, b)
fmt.Println(a, b)
swapPtr(&a, &b)
fmt.Println(a, b)
}
最终输出
2 1
1 2
2 1
2 1
可以看到外部传递的变量的值已经被swapPtr方法修改了。