go uintptr unsafe Pointer offset() 的使用

go语言基本类型

类型名称 有无符号 占用位数
int8 Yes 8
int16 Yes 16
int32 Yes 32
int64 yes 64
uint8 No 8
uint16 No 16
uint32 No 32
uint64 No 64
int Yes 32或64,等于cpu位数
uint No 32或64,等于cpu位数
rune No 32,与uint32等价
byte No 8,与uint8等价
uintptr No 64,与uint64等价

 

 

 

 

 

 

 

 

 

 

 

 

 

 

rune 类型是 Unicode 字符类型,和 uint32 类型等价,通常用于表示一个 Unicode 字符。rune 和 uint32 可以互换使用。

byte 类型与uint8类型等价,byte类型一般用于强调数值是一个原始的内存数据而不是 一个小的整数。

uintptr 是一种无符号的整数类型,与uint64等价,范围:0 ~ 18446744073709551615,足以容纳任何指针值转换来的整型值。 uintptr类型只有在底层编程是才需要,特别是Go语言和C语言函数库或操作系统接口相交互的地方。

不管它们的具体大小,int、uint和uintptr是不同类型的兄弟类型。其中int和int32也是 不同的类型, 即使int的大小也是32bit,在需要将int当作int32类型的地方需要一个显式的类型转换操作,反之亦然

unsafe.Pointer

一个可以指向任意类型的指针,不可以进行数值计算
有四种区别于其他类型的特殊操作:
1. 任意类型的指针值均可转换为 Pointer
2. Pointer 均可转换为任意类型的指针值
3. uintptr 均可转换为 Pointer
4. Pointer 均可转换为 uintptr

示例1(unsafe.Pointer用来取地址,uintptr用来取地址的10进制数值):

package main
import (
	"fmt"
	"unsafe"
)
func main() {
	a := int(5)
	p := &a
	fmt.Println("p:", p)                                                     // a的地址,16进制形式
	fmt.Println("unsafe.Pointer(&a):", unsafe.Pointer(&a))                   // a的地址,16进制形式
	fmt.Println("uintptr(unsafe.Pointer(&a)):", uintptr(unsafe.Pointer(&a))) // a的地址,10进制形式
}

结果如下图:

go uintptr unsafe Pointer offset() 的使用

PS:每次运行a的地址可能是不同的,但 unsafe.Pointer(&a) 和 uintptr(unsafe.Pointer(&a))的值是一样的,只是前者是指针类型,16进制,后者是整数类型,10进制,两者相等,这里 0xc00000a0b8,转换为10进制即 824633761976

示例2(地址计算,偏移,并修改)

package main
import (
	"fmt"
	"unsafe"
)
func main() {
	a := [4]int{0, 1, 2, 3}
	p1 := unsafe.Pointer(&a[1]) // p1指向a[1]的起始地址
	// a[1]的地址类型转换为uintptr类型,后移2个元素的位置,这样移动到a[3]的起始地址位置
	p3 := unsafe.Pointer(uintptr(p1) + 2 * unsafe.Sizeof(a[0]))
	*(*int)(p3) = 6 // 先将Pointer类型转换为*int类型,再赋值
	fmt.Println("a =", a) // 打印出来结果是:a = [0 1 2 6]
}

结果如下图:

go uintptr unsafe Pointer offset() 的使用

PS:unsafe.Pointer 是桥梁,可以让任意类型的指针实现相互转换,也可以将任意类型的指针转换为 uintptr 进行指针运

示例3(unsafe.Offsetof的使用)

该函数返回属性x的起始地址和结构体起始地址的字节数

func Offsetof(x ArbitraryType) uintptr

代码如下:

package main
import (
	"fmt"
	"unsafe"
)
type Person struct {
	name   string
	age    int
	gender bool
}
func main() {
	john := Person{"John", 30, true}
	pp := unsafe.Pointer(&john) // 结构体的起始地址
	pname := (*string)(unsafe.Pointer(uintptr(pp) + unsafe.Offsetof(john.name))) 	// 属性name的起始地址,转换为*string类型
	page := (*int)(unsafe.Pointer(uintptr(pp) + unsafe.Offsetof(john.age)))		 	// 属性age的起始地址,转换为*int类型
	pgender := (*bool)(unsafe.Pointer(uintptr(pp) + unsafe.Offsetof(john.gender)))	// 属性gender的起始地址,转换为*bool类型

	// 进行赋值
	*pname = "Alice"
	*page = 28
	*pgender = false
	fmt.Println(john) // {Alice 28 false}
}

实验结果如下:

go uintptr unsafe Pointer offset() 的使用

上一篇:保证多线程间的可见性,所以声明为volatile


下一篇:并发编程基础进阶(下)