GO语言内存操作指导—unsafe的使用

在unsafe包里面,官方的说明是:
A uintptr is an integer, not a reference.Converting a Pointer to a uintptr creates an integer value with no pointer semantics. Even if a uintptr holds the address of some object, the garbage collector will not update that uintptr's value if the object moves, nor will that uintptr keep the object from being reclaimed.
实际上Uintptr是一个int类型的变量,不是一个引用,没有pointer的语义

INVALID: uintptr cannot be stored in variable before conversion back to Pointer.

 
1
u := uintptr(p)
2
p = unsafe.Pointer(u + offset)

1 官方只建议在如下几种情况下可以使用uintptr

The remaining patterns enumerate the only valid conversions from uintptr to Pointer.

1.1 If p points into an allocated object, it can be advanced through the object by conversion to uintptr, addition of an offset, and conversion back to Pointer.

就是在需要用到地址偏移的操作的时候,将unsafe.pointer转换成uintptr,添加偏移之后,再转回来。

 
1
p = unsafe.Pointer(uintptr(p) + offset)
2
 
3
// The most common use of this pattern is to access fields in a struct
4
// or elements of an array:
5
//
6
// // equivalent to f := unsafe.Pointer(&s.f)
7
f := unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + unsafe.Offsetof(s.f))
8
//
9
// // equivalent to e := unsafe.Pointer(&x[i])
10
e := unsafe.Pointer(uintptr(unsafe.Pointer(&x[0])) + i*unsafe.Sizeof(x[0]))

1.2 Conversion of a Pointer to a uintptr when calling syscall.Syscall

这种我们不涉及

1.3 Conversion of the result of reflect.Value.Pointer or reflect.Value.UnsafeAddr from uintptr to Pointer.

包里面对这一块说明的很清楚:
Package reflect’s Value methods named Pointer and UnsafeAddr return type uintptr instead of unsafe.Pointer to keep callers from changing the result to an arbitrary type without first importing "unsafe". However, this means that the result is fragile and must be converted to Pointer immediately after making the call
由于这种方法返回的不是unsafe.pointer, 因此需要立马转回unsafe.pointer,再需要做这样的操作的时候。

 
1
p := (*int)(unsafe.Pointer(reflect.ValueOf(new(int)).Pointer()))
2
 
3
// INVALID: uintptr cannot be stored in variable
4
// before conversion back to Pointer.
5
u := reflect.ValueOf(new(int)).Pointer()
6
p := (*int)(unsafe.Pointer(u))

1.4 Conversion of a reflect.SliceHeader or reflect.StringHeader Data field to or from Pointer.

As in the previous case, the reflect data structures SliceHeader and StringHeader declare the field Data as a uintptr to keep callers from changing the result to an arbitrary type without first importing “unsafe”. However, this means that SliceHeader and StringHeader are only valid when interpreting the content of an actual slice or string value.
这个用法只在想要获取slice或者string这两个数据类型结构体的各个字段的值的时候是有效的。并不能做其它的使用。

 
1
// In this usage hdr.Data is really an alternate way to refer to the underlying
2
// pointer in the string header, not a uintptr variable itself.
3
var s string
4
hdr := (*reflect.StringHeader)(unsafe.Pointer(&s)) // case 1
5
hdr.Data = uintptr(unsafe.Pointer(p)) // case 6 (this case)
6
hdr.Len = n
7
//
8
// INVALID: a directly-declared header will not hold Data as a reference.
9
var hdr reflect.StringHeader
10
hdr.Data = uintptr(unsafe.Pointer(p))
11
dr.Len = n
12
s := *(*string)(unsafe.Pointer(&hdr)) // p possibly already lost

这种方法虽然官方建议,但本身就是不安全的。不建议使用,在go语言之后的版本这种用法还会被替换掉

 
1
// StringHeader is the runtime representation of a string.
2
// It cannot be used safely or portably and its representation may
3
// change in a later release.
4
// Moreover, the Data field is not sufficient to guarantee the data
5
// it references will not be garbage collected, so programs must keep
6
// a separate, correctly typed pointer to the underlying data.
7
type StringHeader struct {
8
Data uintptr
9
Len int
10
}
11
 
12
// stringHeader is a safe version of StringHeader used within this package.
13
type stringHeader struct {
14
Data unsafe.Pointer
15
Len int
16
}

2 现在我们的使用方法达成的一个共识是uintptr不能作为临时变量保存

我们在需要操作地址偏移的时候,再将unsafe.pointer转换成uintptr。其它指针的传递只能都使用unafe.pointer.

就拿取切片的首地址的操作来说:

2.1先获取切片底层数组的首地址的unsafe.pointer

d62cf27003a945c93a0c_751x211.png@900-0-90-f.png

2.2将unsafe.pointer作为值转递

df11427003a9a6006aeb_745x258.png@900-0-90-f.png

2.3在使用uintptr做地址偏移的时候,千万不要保存成局部变量

8fa1427003a9ee4496d8_730x125.png@900-0-90-f.png

posted @ 2021-11-30 17:18  易先讯  阅读(235)  评论(0编辑  收藏  举报