github.com/code-reading/golang@v0.0.0-20220303082512-ba5bc0e589a3/coding/unsafe/offsetof.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "unsafe" 6 ) 7 8 type Programmer struct { 9 name string 10 language string 11 age int 12 } 13 14 type Modified struct { 15 age int 16 name string 17 language string 18 } 19 20 func main() { 21 m := Modified{10, "stefno", "go"} 22 fmt.Println(m) 23 fmt.Println(unsafe.Offsetof(m.age)) // 0 age在结构体user中的偏移量,也是结构体的地址 24 fmt.Println(unsafe.Offsetof(m.name)) // 8 25 fmt.Println(unsafe.Offsetof(m.language)) // 24 26 27 p := Programmer{"stefno", "go", 10} 28 fmt.Println(p) 29 fmt.Println(unsafe.Offsetof(p.name)) // 0 name在结构体user中的偏移量,也是结构体的地址 30 fmt.Println(unsafe.Offsetof(p.language)) // 16 31 fmt.Println(unsafe.Offsetof(p.age)) // 32 32 33 // 获取结构体的第一个字段地址 34 name := (*string)(unsafe.Pointer(&p)) 35 *name = "update name " // 更新地址下的内容 36 // 获取结构体第二个字段地址 这里需要用到偏移 37 // 注意,这里先通过unsafe.Pointer(&p) 获取结构体第一个字段地址 38 // 但是 Pointer 不能计算, 所以需要先转换为 uintptr 39 // 再加上 unsafe.Offsetof(p.language) 指定字段的偏移 40 // unsafe.Pointer 可以理解返回的是一个*void 空指针 41 // golang 是强类型语言, 使用时必须确定类型 42 // 那么就需要进行类型强转, 因为结构体字段是string 43 // 所以强转指针 *string 44 lang := (*string)(unsafe.Pointer(uintptr(unsafe.Pointer(&p)) + unsafe.Offsetof(p.language))) 45 // 注意 lang 是指针 46 // *lang 才是指针所指地址赋值 47 *lang = "Golang" 48 fmt.Println(p) 49 //下面修改 age 的值 50 // age的偏移, 是要计算上前面的字段的地址长度的 51 // 第一种是继续使用 unsafe.Offsetof 算偏移, 这个是直接算当前字段离结构体起始位置的偏移,已经包含了 52 // 前面字段的长度, 不需要额外添加了 53 // 所以age的起始地址不是 54 // uintptr(unsafe.Pointer(&p)) +unsafe.Offsetof(p.language) + unsafe.Offsetof(p.age) 55 // 而是 unsafe.Pointer(uintptr(unsafe.Pointer(&p)) + unsafe.Offsetof(p.age) 56 age := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&p)) + unsafe.Offsetof(p.age))) 57 *age = 18 58 fmt.Println(p) 59 // 先后端结构体的地址, 60 // unsafe.Sizeof(p.name) + unsafe.Sizeof(language) 得到name 和 languange 占用的字节数 61 // 结构体地址+ 占用的字节数就得到了 p.age 的地址, 转成int 指针 62 pAge := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&p)) + unsafe.Sizeof(p.language) + unsafe.Sizeof(p.name))) 63 *pAge = 20 64 fmt.Println(p) 65 // 第三种也是Sizeof 算大小, 但是传入的是相应字段类型的零值 66 sage := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&p)) + 67 unsafe.Sizeof(string("")) + // 这里p.name 的占字节数 68 unsafe.Sizeof(string("")))) // 这里是 p.language 的占字节数 69 *sage = 30 70 fmt.Println(p) 71 } 72 73 /* 74 output: 75 {10 stefno go} 76 0 77 8 78 24 79 {stefno go 10} 80 0 81 16 82 32 83 {update name Golang 10} 84 {update name Golang 18} 85 {update name Golang 20} 86 {update name Golang 30} 87 */