简单整理一下以下三种指针: 普通指针(*T), uintptr, unsafe.Pointer
1. 普通指针(*T) #
普通指针类型,用于传递对象地址,不能进行指针运算。 可以用 &(取地址) *(根据地址取值)。 这一块都很熟悉了,所以不在做过多的解释了。
package main
import (
"fmt"
)
// 函数式选项模式
type Option func(*Person)
type Person struct {
Name string
Age int
Gender byte
Usage func()
}
func NewPerson(name string,ops ...Option) *Person {
p := &Person{
Name: name,
}
for _,opt := range ops {
opt(p)
}
return p
}
func SetAge(age int) Option {
return func(person *Person) {
person.Age = age
}
}
func SetGender(gender byte) Option {
return func(person *Person) {
person.Gender = gender
}
}
func SetUsage(fn func()) Option {
return func(person *Person) {
person.Usage = fn
}
}
var (
i int = 20
)
func main() {
p := NewPerson("laozhu",
SetAge(10),
SetGender(1),
SetUsage(func() {
fmt.Println("this is person struct")
}),
)
fmt.Printf("%[1]T,%[1]+v\n",p)
p.Filter()
fmt.Printf("%[1]T,%[1]v,%[2]T,%[2]v",i,&i)
}
#output
*main.Person,&{%!+(string=laozhu) %!+(int=10) %!+(uint8=1) %!+(func()=0xc79a00)}v
this is person struct
int,20,*int,0xd26238
uintptr( 无符号的整型 ) #
uintptr是一个无符号的整型,它可以保存一个指针地址。 它可以进行指针运算。 uintptr无法持有对象, GC不把uintptr当指针, 所以uintptr类型的目标会被回收。 想取值需要转成unsafe.Pointer后, 需再转到相对应的指针类型。
uintptr 在 builtin的package里。源代码中是这么解释的
package builtin
//uintptr is an integer type that is large enough to hold the bit pattern of any pointer.
//uintptr是一个能足够容纳指针位数大小的整数类型
type uintptr uintptr
unsafe.Pointer 任意类型的指针 #
unsafe.Pointer可以指向任意类型的指针。 不能进行指针运算,不能读取内存存储的值(想读取的话需要转成相对应类型的指针)。 它是桥梁,让任意类型的指针实现相互转换, 也可以转换成uintptr 进行指针运算。
Pointer是在unsafe的package里。
type Pointer *ArbitraryType
Pointer represents a pointer to an arbitrary type. There are four special operations
available for type Pointer that are not available for other types:
// Pointer代表了一个任意类型的指针。Pointer类型有四种特殊的操作是其他类型不能使用的:
- A pointer value of any type can be converted to a Pointer.
// 任意类型的指针可以被转换为Pointer
- A Pointer can be converted to a pointer value of any type.
// Pointer可以被转换为任务类型的值的指针
- A uintptr can be converted to a Pointer.
// uintptr可以被转换为Pointer
- A Pointer can be converted to a uintptr.
// Pointer可以被转换为uintptr
Pointer therefore allows a program to defeat the type system and read and write
arbitrary memory. It should be used with extreme care.
// 因此Pointer允许程序不按类型系统的要求来读写任意的内存,应该非常小心地使用它。
一般的指针运算会走一下三个步骤。 1.将unsafe.Pointer转换为uintptr => 2.对uintptr执行算术运算 => 3.将uintptr转换回unsafe.Pointer,然后转成访问指向的对象的指针类型
上面步骤中unsafe.Pointer为一级指针、uintptr为二级指针
package main
import (
"fmt"
"unsafe"
)
type fn1 func()
func main() {
t := test()
// 1、unsafe.Pointer(&t) *T转化为unsafe.Pointer指针
// 2、(uintptr)(unsafe.Pointer(&t)) unsafe.Pointer转化为uintptr
// 3、uintptr转化为unsafe.Pointer
// 4、*(*fn1)(addr) 先转化为test函数地址*T,并取地址中的值
addr := unsafe.Pointer((uintptr)(unsafe.Pointer(&t)))
f1 := *(*fn1)(addr)
f1()
fmt.Printf("%+v,%+v,%+v",addr,&t,f1)
}
func test() fn1 {
return func() {
fmt.Println("aaaaaaaa")
}
}
#output
aaaaaaaa
0xc000006028,0xc000006028,0xa79660
unsafe包
type ArbitraryType int
type Pointer *ArbitraryType
//值所对应变量在内存中的大小
func Sizeof(x ArbitraryType) uintptr
//结构体中成员的偏移量(实践代码中namePointer := unsafe.Pointer(uintptr(unsafe.Pointer(&user)) + unsafe.Offsetof(user.name))
func Offsetof(x ArbitraryType) uintptr
//值所对应的变量在内存地址中的几个字节对齐
func Alignof(x ArbitraryType) uintptr
实践 #
可以看看下面测试, 数组和struct是可以使用指针偏移指向下一个元素
package main
import (
"fmt"
"unsafe"
)
type user struct {
id int
age int
name string
}
func main() {
a := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
b := unsafe.Pointer(uintptr(unsafe.Pointer(&a[0])) + 9*unsafe.Sizeof(a[0]))
// b是 unsafe.Pointer 所以可转任意指针,转成(*int)指针后在取值
fmt.Printf("b: %v, unsafe.Sizeof(a[0]): %d\n", *(*int)(b), unsafe.Sizeof(a[0]))
//b: 9, unsafe.Sizeof(a[0]): 8
c := unsafe.Pointer(uintptr(unsafe.Pointer(&a)) + uintptr(16))
//int是8位长度 所以16 等于 16/8 挪动了2位,所以下面结果是2
fmt.Printf("c: %v\n", *(*int)(c)) //c: 2
user := user{id: 1, age: 10, name: "user1"}
namePointer := unsafe.Pointer(uintptr(unsafe.Pointer(&user)) + unsafe.Offsetof(user.name))
//这也一样 name是 unsafe.Pointer 所以可转任意指针,转成(*string)指针后在取值
fmt.Printf("name: %v\n", *(*string)(namePointer)) //name: user1
}
再看看slice和map相关的简单测试
//因slice的结构是 => |ptr|len|cap
s := make([]int, 5, 10)
var Len = *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + uintptr(8)))
//挪一个位置是Len
fmt.Println(Len, len(s))
// 5 5
var Cap = *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + uintptr(16)))
//挪二个位置是CAP
fmt.Println(Cap, cap(s))
// 10 10
mp := make(map[string]int)
mp["a"] = 11
mp["b"] = 22
//因map结构中第一个是元素个数,所以可以直接转成len
count := **(**int)(unsafe.Pointer(&mp))
fmt.Println(count, len(mp)) // 2 2