• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
思念以南
博客园    首页    新随笔    联系   管理    订阅  订阅

unsafe 指针转换与内存操作

Golang 提供了 unsafe 包,让我们能够直接操作指定内存地址的内存。

通过 unsafe.Pointer() 函数,我们能够获取变量的内存地址表示,本质上这是个整数。可以将任意变量的地址转换成 Pointer 类型,也可以将 Pointer 类型转换成任意的指针类型,它是不同指针类型之间互转的中间类型。

但 Pointer 不支持运算,如果要在内存地址上进行加减运算,需要将其转为 uintptr 类型。

下面我们尝试读取切片地址,并通过内存操作遍历其内容:

package main

import "fmt"
import "unsafe"

func main() {
	// head = {address, 10, 10}
	// body = [1,2,3,4,5,6,7,8,9,10]
	var s = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	var address = (**[10]int)(unsafe.Pointer(&s))
	var len = (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + uintptr(8)))
	var cap = (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + uintptr(16)))
	fmt.Println(address, *len, *cap)
	var body = **address
	for i := 0; i < 10; i++ {
		fmt.Printf("%d ", body[i])
	}
}
----------
0xc000004460 10 10
1 2 3 4 5 6 7 8 9 10 


上述代码中:

  • unsafe.Pointer(&s) 获取切片 s 底层表示的第一个位置的内存地址,也即底层数组的地址存放地址,
  • 通过 (**[10]int)(unsafe.Pointer(&s)) 将其转为 **[10]int 类型指针,又通过 **addrss 还原为数组;
  • unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + uintptr(8)) 通过地址运算,获得 length 的存放地址,进而通过 (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + uintptr(8))) 将 length 内存转为 int 指针
    最后通过 *len 获取切片长度;
    对于 cap 的操作与 len 类似,不再赘述;

总之:

  • 通过 unsafe,我们能够实现内存地址在不同指针类型间的转换,进而更灵活地操作内存;
  • 本实验也进一步验证了切片的底层存储结构;
  • unsafe 在不是必须的条件下应该少使用,直接操作内存毕竟是风险较大的;
posted @ 2022-05-10 14:59  思念以南  阅读(187)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3