引言
Golangunsafe.Pointeruintptrunsafe.SizeofGolangunsafe
unsafe包
unsafeunsafeGo
JavaunsafeunsafeGo
unsafe构成
可以看到,包的构成比较简单,下面我们主要结合源码中注释内容来展开剖析和学习。
type ArbitraryType int
Arbitrary
ArbitraryTypeunsafeGo
type Pointer *ArbitraryType
Pointerunsafe
灵活转换
它表示指向任意类型的指针,有四种特殊操作可用于类型指针,而其他类型不可用,大概的转换关系如下:
PointerPointeruintptrPointerPointeruintptr
潜在的危险性
Pointer
源码注释中列举了提到了一些正确和错误使用的例子。它还提到更为重要的一点是:不使用这些模式的代码可能现在或者将来变成无效。即使下面的有效模式也有重要的警告。试图来理解下这句话的核心就是,它不能对你提供什么保证!
go vetgo vet
go vetgolangfmt.Printf%dgo vet
代码样例:
func TestErr(t *testing.T) {
fmt.Printf("%d","hello world")
}
运行:
`go vet unsafe/unsafe_test.go`
控制台输出提示:
unsafe/unsafe_test.go:9:2: Printf format %d has arg "hello world" of wrong type string
✅ 正确的使用姿势
Pointer
- (1) 指针 *T1 转化为 指针 *T2. T1、T2两个变量共享等值的内存空间布局,在不超过数据范围的前提下,可以允许将一种类型的数据重新转换、解释为其他类型的数据。
下面我们操作一个样例:声明并开辟一个内存空间,然后基于该内存空间进行不同类型数据的转换。
代码如下:
Pointer
(2) Pointer 转换为 uintptr(不包括返回的转换)
uintptruintptruintptruintptruintptruintptruintptruintptruintptr
uintptr
小结
最常见的用途是访问结构或数组元素中的字段:
&^
❌ 错误的使用姿势
与C中不同的是,将指针指向到其原始分配结束之后是无效的:
注意,两个转换必须出现在同一个表达式中,它们之间只有中间的算术运算。
请注意,指针必须指向已分配的对象,因此它不能是零。
syscall.SyscalluintptrsyscallSyscalluintptruintptr
uintptr
uintptr
要使编译器识别此模式,转换必须出现在参数列表中:
uintptrPointerReflectReflect.Value.PointerReflect.Value.UnsafeAddr
reflectPointerUnsafeAddruintptrunsafeunsafePointer
与上述情况一样,在转换之前存储结果是无效的
reflect.SliceHeaderreflect.StringHeaderPointerreflect.SliceHeaderreflect.StringHeaderuintptrunsafe
SliceHeaderStringHeaderslicestring
hdr.Datauintptr
reflect.SliceHeaderreflect.StringHeaderslicestring*reflect.SliceHeader*reflect.StringHeader
func Sizeof(x ArbitraryType) uintptr
Sizeofvv
Go语言中非聚合类型通常有一个固定的大小
引用类型或包含引用类型的大小在32位平台上是4字节,在64位平台上是8字节。
类型 | 分类 | 大小 |
---|---|---|
bool | 非聚合 | 1个字节 |
intN, uintN, floatN, complexN | 非聚合 | N/8个字节(例如float64是8个字节) |
int, uint, uintptr | 非聚合 | 1个机器字 (32位系统:1机器字=4字节; 64位系统:1机器字=8字节) |
*T | 聚合 | 1个机器字 |
string | 聚合 | 2个机器字(data,len) |
[]T | 聚合 | 3个机器字(data,len,cap) |
map | 聚合 | 1个机器字 |
func | 聚合 | 1个机器字 |
chan | 聚合 | 1个机器字 |
interface | 聚合 | 2个机器字(type,value) |
func Offsetof(x ArbitraryType) uintptr
Offsetofvf
内存对齐 计算机在加载和保存数据时,如果内存地址合理地对齐的将会更有效率。由于地址对齐这个因素,一个聚合类型的大小至少是所有字段或元素大小的总和,或者更大因为可能存在内存空洞。\
内存空洞 编译器自动添加的没有被使用的内存空间,用于保证后面每个字段或元素的地址相对于结构或数组的开始地址能够合理地对齐
bool、string、int16
以上是针对单个结构体内的内存对齐的测试演示,当多个结构体组合在一起时还会产生内存对齐,感兴趣可以自行实践并打印内存偏移量来观察组合后产生的内存空洞。
func Alignof(x ArbitraryType) uintptr
Alignofvvf
不同类型有着不同的内存对齐方式,总体上都是以最小可容纳单位进行对齐的,这样可以在兼顾以最小的内存空间填充来换取内存计算的高效性。
参考