七叶笔记 » golang编程 » golang基础之指针和unsafe包

golang基础之指针和unsafe包

一、golang指针及其限制

1.与 C语言 相比,在Go语言中没有最复杂的指针运算部分,只留下了获取指针(&运算符)和获取对象(*运算符)的运算。而且Go语言中没有->操作符来调用指针所属的成员,而与一般对象一样,都是使用.来调用。

2.Go语言中一个指针默认值为 nil

3.golang指针限制

1)Go的指针不能进行数学运算

2)不同类型的指针不能相互转换

3)不同类型的指针不能使用 == 或 != 比较

4)不同类型的指针变量不能相互赋值

二、unsafe包

golang指针是类型安全的,但存在很多限制。因此Go还提供了一种非类型安全的指针,即unsafe.Pointer。

unsafe.Pointer可以指向任意类型,类似于 C 语言里的 void*。在unsafe包中除了Pointer外还有其他三个函数:

func Sizeof(x ArbitraryType) uintptr:返回类型 x 所占据的字节数,但不包含 x 所指向的内容的大小。例如,对于一个指针,函数返回的大小为 8 字节(64位机上),一个 slice 的大小则为 slice header 的大小。

func Offsetof(x ArbitraryType) uintptr:返回 结构体 成员在内存中的位置离结构体起始处的字节数,所传参数必须是结构体的成员。

func Alignof(x ArbitraryType) uintptr:返回 m,m 是指当类型进行内存对齐时,它分配到的内存地址能整除 m。

以上三个函数返回的结果都是 uintptr 类型,这和 unsafe.Pointer 可以相互转换。

pointer 不能直接进行数学运算,但可以把它转换成 uintptr,对 uintptr 类型进行数学运算,再转换成 pointer 类型。

注意:uintptr 并没有指针的语义,意思就是 uintptr 所指向的对象会被 gc 无情地回收。而 unsafe.Pointer 有指针语义,可以保护它所指向的对象在“有用”的时候不会被垃圾回收。

三、unsafe的应用示例

1.使用unsafe获取slice长度

type slice struct {

array unsafe.Pointer // 元素指针

len int // 长度

cap int // 容量

}

package main

import “fmt”

func main() {

s := make([]int, 10)

var Len = *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + uintptr(8))) //先转为uintptr,然后进行指针运算,移动slice的第二个长度的成员变量上,获取其指针指向的值

fmt.Println(Len, len(s)) // 10 10

}

2.通过unsafe.Offsetof方法获取成员 偏移量

对于一个结构体,通过 offset 函数可以获取结构体成员的偏移量,进而获取成员的地址,读写该地址的内存,就可以达到改变成员值的目的。结构体在分配内存时会得到一块连续的内存空间,结构体的地址也代表了第一个成员的地址。

package main

import (

“fmt”

“unsafe”

)

type Student struct {

name string

sex string

}

func main() {

p := Student{“zhangsan”, “male”}

fmt.Println(p) //{stefno male}

name := (*string)(unsafe.Pointer(&p))

*name = “lisi”

sex := (*string)(unsafe.Pointer(uintptr(unsafe.Pointer(&p)) + unsafe.Offsetof(p.sex)))

*sex = “female”

fmt.Println(p) //{lisi female}

}

相关文章