Ezreal 书架 Ezreal 书架
Home
  • 《Go程序员面试笔试宝典》
  • 《RabbitMQ 实战指南》
  • 《深入理解kafka》
  • MySQL45讲
  • 透视HTTP协议
  • 结构化数据的分布式存储系统
  • Raft 共识算法
Home
  • 《Go程序员面试笔试宝典》
  • 《RabbitMQ 实战指南》
  • 《深入理解kafka》
  • MySQL45讲
  • 透视HTTP协议
  • 结构化数据的分布式存储系统
  • Raft 共识算法
  • 逃逸分析

  • 延迟语句

  • 数据容器

  • 通道

  • 接口

  • unsafe

    • 如何利用 unsafe 包修改私有成员
    • 如何利用 unsafe 获取 slice 和 map 的长度
    • 如何实现字符串和 byte 切片的零复制转换
  • context

  • Go程序员面试笔试宝典
  • unsafe
ezreal_rao
2023-06-01

如何利用 unsafe 包修改私有成员

对于一个结构体,通过 offset 函数可以获取结构体成员的偏移量,进而获取成员的地址,读写该地址的内存,就可以达到改变成员值的目的。

这里有一个内存分配相关的事实:结构体会被分配一块连续的内存,结构体的地址也代表了第一个成员的地址。

来看一个例子:

package main

import (
	"fmt"
	"unsafe"
)

type Programmer struct {
	name     string
	language string
}

func main() {
	p := Programmer{"stefno", "go"}
	fmt.Println(p)

	name := (*string)(unsafe.Pointer(&p))
	*name = "qcrao"

	lang := (*string)(unsafe.Pointer(uintptr(unsafe.Pointer(&p)) + unsafe.Offsetof(p.language)))
	*lang = "Golang"
	fmt.Println(p)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

运行代码,输出:

{stefno go}
{qcrao Golang}
1
2

字段 name 是结构体的第一个成员, 因此可以直接将 &p 解析成 *string , 接着直接修改 name 的值。有了第一个字段的地址后,可以通过 offset 求出第二个字段的地址:

lang := (*string)(unsafe.Pointer(uintptr(unsafe.Pointer(&p)) + unsafe.Offsetof(p.language)))
1

先将 unsafe.Pointer 转成 uintptr,然后进行数学计算,再转换为 unsafe.Pointer,最后再转成字符串指针。

对于结构体的私有成员,现在有办法可以通过 unsafe.Pointer 改变它的值了。把 Programmer 结构体升级,多加一个字段:

type Programmer struct {
    name string
    age int
    language string
}
1
2
3
4
5

并且放在其他包里,这样在 main 函数中,它的三个字段都是私有成员变量,不能直接修改, 也不能对字段取 offset。但可以通过 unsafe.Sizeof () 函数可以获取成员大小,进而计算出成员的地址,直接修改内存。

func main() {
    p := Programmer{"stefno", 18, "go"}
    fmt.Println(p)
    lang := (*string)(unsafe.Pointer(uintptr(unsafe.Pointer(&p)) + unsafe.Sizeof(int(0)) + unsafe.Sizeof(string(""))))
    *lang = "Golang"
    fmt.Println(p)
}
1
2
3
4
5
6
7

输出如下:

{stefno 18 go}
{stefno 18 Golang}
1
2
#golang#unsafe
上次更新: 7/12/2024, 2:37:05 PM
如何让编译器自动检测类型是否实现了接口
如何利用 unsafe 获取 slice 和 map 的长度

← 如何让编译器自动检测类型是否实现了接口 如何利用 unsafe 获取 slice 和 map 的长度→

最近更新
01
为什么我的MySQL会抖一下
07-15
02
HTTP 性能优化面面观
07-12
03
WebSocket:沙盒里的 TCP
07-12
更多文章>
Theme by Vdoing | Copyright © 2022-2024 Ezreal Rao | CC BY-NC-SA 4.0
豫ICP备2023001810号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式