package main
import (
"fmt"
"unsafe"
)
type person struct {
name string
age int
}
func (p *person) String() string {
return fmt.Sprintf("{name:%s} {age:%d}", p.name, p.age)
}
func main() {
p := new(person)
name := (*string)(unsafe.Pointer(uintptr(unsafe.Pointer(p))))
age := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + unsafe.Sizeof(16)))
*age = 40
*name = "Jackie Chan"
fmt.Println(p)
}
string的底层数据结构是一个结构体,包含一个指向底层数组的指针和一个长度字段。在x64平台上,这两个字段都是占用8个字节。因此string一共占用16个字节。
name字段(一个string)占据了前16个字节,紧接着是age字段(一个int),它开始于第16个字节之后的位置。因此,想要通过指针访问age字段时,需要给person结构体的基地址加上16字节的偏移量。
可以验证一下:
package main
import (
"fmt"
"unsafe"
)
func main() {
s := "hello world"
arr := (*[]byte)(unsafe.Pointer(&s))
l := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + 8))
fmt.Println(string(*arr), *l)
}
通过unsafe修改字符串的长度:
package main
import (
"fmt"
"unsafe"
)
func main() {
s := "hello world"
fmt.Println(len(s))
l := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + 8))
*l = 0
fmt.Println(len(s))
}
8字节是指向底层数组的指针的占用大小。