这一期分享一下GO语言中的指针,指针在C语言和C++语言中较为常见,但往往也是学习的难点之一,指针用好了能增加运行效率,用不好就很容易造成内存泄漏。排查起来很痛苦。GO语言中的指针入门很容易,使用也很简单。GO语言中指针分为安全指针,不安全指针,和整数指针。下面将分别进行介绍,这一期,一次性把GO语言的指针讲清楚。
一、安全指针
顾名思义,就是可以安全使用的指针,但它也有前提,使用前依然要进行初化,判空。否则会出现panic错误的。看下面例子:
var intPtr *int //这里如果这样声明了,系统默认是nil
i := 1
intPtr = &i //获取i的指针给intPtr
if intPtr != nil {
fmt.Println(*intPtr)
}
//当然也可以下面的方式
intPtr2 := &i //自动推导出类型*int
//使用时
if intPtr2 != nil {
fmt.Println(*intPtr2) //*取实际值
}
二、非安全指针
有安全指针当然对应的也有非安全指针。非安全指针有点类似C语言中的void*指针。是通用的,可以转换成其它类型的安全指针。如果你有场景需要一个函数接收不同类型的参数作为返回值就可以此方法,看下面例子:
func main() {
var i int
unsafePtr := unsafe.Pointer(&i)
*((*int)(unsafe.Pointer(&i))) = 1
fmt.Println(i)
var str string
unsafePtr = unsafe.Pointer(&str)
*((*string)(unsafePtr)) = "string data"
fmt.Println(str)
}
上面例子实际使用中是可以使用any来代替的,any自身带有type类型,这里只是为了展示一下unsafe.Pointer这个指针,可以做为通用的,又可以转为实际对应的类型指针。
三、整数指针
整数指针是GO语言较为底层,或者更为低级类似像C语言的一类指针,是可以进行指针加减的。
整数指针是不能正常使用的,它没有类型,在进行了加减之后还需要转换为非安全指针,非安全指针再转换为实际的类型。看下面例子,使用C风格遍历操作切片:
func main() {
intSlice := []int{1, 2, 3, 4, 5, 6, 7}
intSliceHdr := (*reflect.SliceHeader)(unsafe.Pointer(&intSlice))
intUPtr := intSliceHdr.Data
for i := 0; i < intSliceHdr.Len; i++ {
fmt.Println(*(*int)(unsafe.Pointer(intUPtr + uintptr(i)*unsafe.Sizeof(intSlice[0]))))
}
}
上面例子中,使用reflect反射包中的SliceHeader,下面可以看一下这个结构的原型:
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
可以看到Data的类型就是uintptr,Len表示当前拥有的数据长度,Cap是预分配空间扩容使用。
四、指针的转换原则
通过上面的例子可以看出以下原则:
- 类型安全指针可以转换为非安全指针
- 非安全指针可以转换为整数指针,可用于指针算术,但要注意加减的长度。
- 非安全指针可以转换为整数指针,也可以转换为类型安全指针,它是一个桥梁。
总结一下:安全指针就大胆的使用,不要怕,它是最常用的。非安全指针使用时往往是搭配反射和整数指针来使用,更为低级,难度稍大。在C语言与GO语言交互过程中,发挥很大的作用。可以轻松的使用GO的数据或者切片与C数组互转换。甚至可以把地址传出GO的空间给C使用。
好了,这一期分享到这里,感谢大家支持。