Go语言标准库学习之unsafe——不安全的标准库(不同类型指针之间的转换)

本文深入探讨了Go语言中的unsafe包,介绍了其核心类型和方法,包括Pointer和uintptr的使用,以及Sizeof、Alignof和Offsetof函数的功能。通过实例展示了如何进行指针类型转换和复杂的结构体操作,强调了使用unsafe包时的潜在风险。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

unsafe包提供了一些跳过go语言类型安全限制的操作。这个标准库用的比较少,这篇博客向大家介绍以下,如何使用unsafe包,希望对你有帮助。

1. unsafe包介绍
(1)两种重要类型
  • ArbitraryType

    // ArbitraryType表示任意一种类型,但并非一个实际存在与unsafe包的类型。
    type ArbitraryType int
    
  • Pointer

    // Pointer类型用于表示任意类型的指针。
    type Pointer *ArbitraryType
    

    有4个特殊的只能用于Pointer类型的操作:

    • 任意类型的指针可以转换为一个Pointer类型值。
    • 一个Pointer类型值可以转换为任意类型的指针。
    • 一个uintptr类型值可以转换为一个Pointer类型值。
    • 一个Pointer类型值可以转换为一个uintptr类型值。

    因此,Pointer类型允许程序绕过类型系统读写任意内存。使用它时必须谨慎。

(2)三个方法

  • Sizeof

    // Sizeof返回类型v本身数据所占用的字节数。
    // 返回值是“顶层”的数据占有的字节数。
    // 例如,若v是一个切片,它会返回该切片描述符的大小,而非该切片底层引用的内存的大小。
    func Sizeof(v ArbitraryType) uintptr
    
  • Alignof

    // Alignof返回类型v的对齐方式(即类型v在内存中占用的字节数)
    // 若是结构体类型的字段的形式,它会返回字段f在该结构体中的对齐方式。
    func Alignof(v ArbitraryType) uintptr
    
  • Offsetof

    // Offsetof返回类型v所代表的结构体字段在结构体中的偏移量,它必须为结构体类型的字段的形式。
    // 换句话说,它返回该结构起始处与该字段起始处之间的字节数。
    func Offsetof(v ArbitraryType) uintptr
    
2. unsafe包使用
(1)指针类型转换
package main

import (
	"fmt"
	"unsafe"
)

func main() {
	// 定义两个不同类型的变量u和i
	u := uint32(32)
	i := int32(1)
	// 输出地址
	fmt.Println(&u, &i)
	p := &i
	// 修改p的指针类型
	p = (*int32)(unsafe.Pointer(&u))
	fmt.Println(p)
	fmt.Println(*p)
}

测试结果:

$ go run main.go
0xc000070058 0xc00007005c
0xc000070058
32
(2)更复杂的示例

在看代码前,我们先来了解一下结构体的一些基本概念:

  • 结构体的成员变量在内存存储上是一段连续的内存
  • 结构体的初始地址就是第一个成员变量的内存地址
  • 基于结构体的成员地址去计算偏移量。就能够得出其他成员变量的内存地址
package main

import (
	"fmt"
	"unsafe"
)

type Student struct {
	Name string
	Age  int64
}

func main() {
	s := Student{Name: "random_w", Age: 18}
	sPointer := unsafe.Pointer(&s)
	// 修改Name变量的值,因为结构体存储在内存上的一段连续内存
	// 因此我们直接取出指针后转换为 Pointer,再强制转换为字符串类型的指针值就可以获取第一个变量Name的地址
	NamePointer := (*string)(unsafe.Pointer(sPointer))
	// 修改该地址的值
	*NamePointer = "random_w_update"
	// 通过给sPointer指针的地址加上偏移量就可以获取到第二个变量的地址。
    // unsafe.Offsetof会获取回类型v所代表的结构体字段在结构体中的偏移量
    // uintptr 是 Go 的内置类型。返回无符号整数,可存储一个完整的地址,后续常用于指针运算。
	AgePointer := (*int64)(unsafe.Pointer(uintptr(sPointer) + unsafe.Offsetof(s.Age)))
	*AgePointer = 19

	fmt.Printf("s.Name: %s, s.Age: %d", s.Name, s.Age)
}

测试结果:

$ go run main.go
s.Name: random_w_update, s.Age: 19
总结
  • unsafe.Pointer 可以让你的变量在不同的指针类型转来转去,也就是表示为任意可寻址的指针类型。
  • uintptr 常用于与 unsafe.Pointer 打配合,用于做指针运算。

注意:没有特殊必要的话。是不建议使用 unsafe 标准库,它并不安全

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值