前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >go: 无用之技 - 修改Private成员

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

作者头像
超级大猪
发布2022-11-21 15:40:26
3170
发布2022-11-21 15:40:26
举报
文章被收录于专栏:大猪的笔记大猪的笔记

孔乙已尚且知道回的四种写法,作为逗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("%v\n", tp)
  10. fmt.Printf("%v\n", 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("%v\n", tp3)
  16. }

输出:

  1. &{ }

今天,你学会了吗?

FBI warning

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-11-14 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 准备结构体
  • 法1: 汇编大法
    • 在main中声明函数
    • 法2
    • FBI warning
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档