go: 无用之技 - 修改Private成员

2022-11-21 15:40:26 浏览数 (2)

孔乙已尚且知道回的四种写法,作为逗B程序员,怎么能不学点无用之技傍身?

本文是这些奇技淫巧的第一弹(也许没有第二弹),个人认识浅薄,如有错误,概不负责。:)

废话不说,今天来学习如何修改go结构体中的private(不可导出)值。

请看栗子

准备结构体

  1. package changestruc
  2. func NewMyStr(a, b int) *MyStr {
  3. return &MyStr{a, b}
  4. }
  5. type MyStr struct {
  6. a int // 8
  7. b int // 8
  8. }

假设有结构如上,在go中,小写的变量是不可导出的。

在main中,new出对象如下:

  1. tp := changestruc.NewMyStr(, )
  2. tp1 := changestruc.NewMyStr(13, 15)

很显然,a,b两个值在后续,正常方法都不能修改。

本文中,将会在main中执行神奇的。

  1. tp.a = tp.a tp.b

a 的值改为 a b的值。

法1: 汇编大法

简单讲一下原理,传入的结构体MyStr在内存中长这样:

  1. type MyStr struct{
  2. a int 8byte
  3. b int 8byte
  4. }

go会按8byte做字节对齐,如果是uint8等结构会有影响,具体请搜索go 汇编

当知道结构的内存布局,就可以用汇编来对它进行操作,具体请参考注释。

在main.go建同级文件main.s

  1. #include "textflag.h"
  2. #include "funcdata.h"
  3. // func Unsafe_Mod(in *MyStr)
  4. TEXT ·Unsafe_Mod(SB), NOSPLIT, $0-8 // TEXT 定义一个函数, 8是argSize,因为传入的是指针,正好8byte
  5. MOVQ a 0(FP), AX //FP函数的帧指针,一般用来访问函数的参数和返回值,等同 AX = in
  6. MOVQ (AX), CX // 打括号是为了取值,因为传入的是指针,等同 CX = *AX[0,8]
  7. ADDQ 8(AX), CX // 同理,此句等同 CX = *AX[8,16]
  8. MOVQ CX, (AX) // *AX[0,8] = CX
  9. RET

在main中声明函数

  1. package main
  2. // 申明函数,它在.s中实现
  3. func Unsafe_Mod(in *changestruc.MyStr)
  4. func main() {
  5. tp := changestruc.NewMyStr(12, 14)
  6. tp1 := changestruc.NewMyStr(13, 15)
  7. Unsafe_Mod(tp)
  8. Unsafe_Mod(tp1)
  9. fmt.Printf("%vn", tp)
  10. fmt.Printf("%vn", tp1)
  11. }

执行go run main (记得先 go mod init main)

输出:

  1. &{ }
  2. &{28 15}

大功告成了。

法2

原理和法1类似,还是直接操作结构的内存,代码非常简单:

  1. package main
  2. import (
  3. "fmt"
  4. "main/changestruc"
  5. "unsafe"
  6. )
  7. func main(){
  8. tp3 := changestruc.NewMyStr(12, 14)
  9. ptr := unsafe.Pointer(tp3) // 找到结构体的地址
  10. ptrToA := unsafe.Pointer(uintptr(ptr) uintptr(0)) // a 的地址
  11. ptrToIntA := (*int)(ptrToA) // a 的值
  12. ptrToB := unsafe.Pointer(uintptr(ptr) uintptr(8))
  13. ptrToIntB := (*int)(ptrToB)
  14. *ptrToIntA = *ptrToIntA *ptrToIntB // 直接修改值
  15. fmt.Printf("%vn", tp3)
  16. }

输出:

  1. &{ }

今天,你学会了吗?

FBI warning

上述这样的代码十分的机械,假设后续结构体发生了改变,则内存的寻址很可能错误。功能将不再正常。:)

0 人点赞