首頁>技術>

對於一個結構體,透過 offset 函式可以獲取結構體成員的偏移量,進而獲取成員的地址,讀寫該地址的記憶體,就可以達到改變成員值的目的。

這裡有一個記憶體分配相關的事實:結構體會被分配一塊連續的記憶體,結構體的地址也代表了第一個成員的地址。

我們來看一個例子:

 1      2      3      4      5      6      7      8      9     10     11     12     13     14     15     16     17     18     19     20     21     22     23     24

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

{stefno go}     {qcrao Golang}

name 是結構體的第一個成員,因此可以直接將 &p 解析成 *string。這一點,在前面獲取 map 的 count 成員時,用的是同樣的原理。

對於結構體的私有成員,現在有辦法可以透過 unsafe.Pointer 改變它的值了。

我把 Programmer 結構體升級,多加一個欄位:

1     2     3     4     5

type   Programmer struct {             name string             age int             language string     }

並且放在其他包,這樣在 main 函式中,它的三個欄位都是私有成員變數,不能直接修改。但我透過 unsafe.Sizeof() 函式可以獲取成員大小,進而計算出成員的地址,直接修改記憶體。

1     2     3     4     5     6     7     8     9

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

{stefno 18 go}     {stefno 18 Golang}

5
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • golang2021資料格式(96)Go語言垃圾回收和SetFinalizer