说到指针,许多人感到“谈指针色变”,对指针偏移、指针运算、指针转换非常恐惧。其实,指针是C族语言(C/C++)极高性能的根本,在操作大块数据和做内存地址偏移时,方便快捷。C族语言(C/C++)中指针被诟病的原因是 指针的运算
和 内存释放
。
go中的指针分为两个重要方面:
-
类型指针。类型指针拥有指针的高效访问:传递数据使用指针,而不是采用内存拷贝;使用类型指针修改(其指针地址)指向的普通变量值。类型指针不能运算和偏移。
-
切片。由指向起始元素的原始指针、元素数量和容量构成。切片比原始指针更安全高效,当切片越界时,go运行时会报panic,并打出堆栈,原始指针只能崩溃
1. 几个名词和过程
- 普通变量
初始化普通变量,类型为T。
- 取(内存)地址
使用&操作符,对普通变量取(内存)地址,得到普通变量的指针(变量)。
- 指针(变量)
对普通变量取地址后(&(普通变量)
),得到指针变量。在程序运行时,每个变量都会被分配内存,拥有内存地址。指针(变量)的值就是这个内存地址,也叫指针地址,这就是指针(变量)的本质。
注: 每个普通变量都有内存地址,指针的值就是这个内存地址。
- 指针类型
使用&(普通变量),对普通变量取(内存)地址后,得到指针变量,其类型为*T,即T的指针类型。
- 指针地址
指针变量的值是指针地址。指针地址带有"0x"的十六进制前缀。指针地址在64位平台上是64位内存地址,在32位平台上是32位地址。
- 指针(变量)取值
使用*
操作符,对指针变量取值,从指针变量中获取(指针地址指向的)原普通变量的值。
- 操作符
&
和*
取(内存)地址操作符&
和取(指针变量)值操作符*
是一对互补操作符:
1)使用&
取出普通变量的(内存)地址得到指针变量
2)使用*
对指针变量进行取值操作,获得指针指向的原普通变量值
3)*(*T)
取出指针变量指向的原普通变量值,即*(*T)
指向原来T类型的普通变量
2. 指针(变量):取指针地址(&)和指针类型(*T)
在程序运行时,每个变量都拥有一个(内存)地址,这个地址表示变量在内存中的位置。在go中,我们使用&
对变量进行取地址
操作。格式如下:
ptr := &ins // 普通变量ins是T类型
ins表示普通变量,类型为T。使用&取内存地址,得到T的指针类型*
T,赋给指针变量ptr(*T类型,T的指针类型)。*
表示指针。
package main
import "fmt"
func main() {
var cat int = 1
var str string = "orange"
// 打印指针变量的内存地址
fmt.Printf("%p %p", &cat, &str)
}
// 0xc0000b8030
// 0xc0000ae030
指针值带有"0x"的十六进制前缀。输出普通变量的指针地址每次都不同。
3. 从指针(变量)中获取(指针指向的)原普通变量的值
对指针使用*,即对指针取值。
package main
import (
"fmt"
"testing"
)
func main() {
// var house = "mabuli point 10880, 90265"
// house := "mabuli point 10880, 90265"
var house string = "mabuli point 10880, 90265" // 初始化一个字符串类型的变量house
ptr := &house // 对普通变量取内存地址,得到字符串指针类型,赋给指针变量ptr,是*string类型
fmt.Printf("ptr type: %T\n", ptr) // 获取指针变量的类型
fmt.Printf("address: %p\n", ptr) // 获取指针变量的内存地址
value := *ptr // 对指针变量取值
fmt.Printf("value type: %T\n", value)
fmt.Printf("address: %s\n", value)
}
/*
ptr type: *string
address: 0xc0000ae030
value type: string
address: mabuli point 10880, 90265
*/
4. 使用指针修改值(常用)
使用指针不仅可以获取普通变量对应的(内存)地址,还可以用来修改指针指向的普通变量值。
package ptr_test
import (
"fmt"
"testing"
)
// 使用指针进行数值交换:交换两个指针变量对应的(普通变量)值
func swap(a, b *int) {
t := *a // 取a指针对应的普通变量值,赋给临时变量t
*a = *b // 取b指针对应的普通变量值,赋给a指针对应的普通变量
*b = t // 把a指针对应的普通变量值,赋给b指针指向的变量
// b, a = a, b 交换指针变量的值,只是交换指针地址,并没交换指针地址指向的普通变量值
}
func TestPtrReviseCode(t *testing.T) {
x, y := 1, 2
swap(&x, &y) // 取普通变量的地址,得到指针变量,是指针类型*T,作为swap的入参
fmt.Println(x, y)
}
// 2 1
*指针变量
放在 :=
的右侧,表示取指针变量指向的普通变量值,如 t := *a
,取指针a指向的变量值,赋给变量t ;
当 *指针变量
放在 =
的左侧,表示把值设置给指向的变量,如 *b = t
,把普通变量t的值,赋给指针b指向的变量。
5. 使用new( )函数创建指针(常用)
使用 new( ) 函数可以创建一个指针变量(初始化一个指针),得到一个对应类型的指针。创建过程会分配内存,指针指向的值为默认值。格式如下:
ins := new(类型)
创建一个指针变量str,它是一个字符串类型的指针。
package main
import "fmt"
func main() {
str := new(string)
*str = "ninja"
fmt.Println(*str)
}
// ninja