zoukankan      html  css  js  c++  java
  • 【转】go uintptr unsafe Pointer offset() 的使用

    原文:  https://blog.csdn.net/yzf279533105/article/details/97143100

     package main
     import (
       "fmt"
       "unsafe"
       "log"
     )
     type Person struct {
       name   string
       age    int
       gender bool
       s rune
     }
     func main() {
       john := Person{"John", 30, true, 32}
       pp := unsafe.Pointer(&john) // 结构体的起始地址
       pname := (*string)(unsafe.Pointer(uintptr(pp) + unsafe.Offsetof(john.name)))  // 属性name的起始地址,转换为*string类型
       page := (*int)(unsafe.Pointer(uintptr(pp) + unsafe.Offsetof(john.age)))     // 属性age的起始地址,转换为*int类型
       pgender := (*bool)(unsafe.Pointer(uintptr(pp) + unsafe.Offsetof(john.gender)))  // 属性gender的起始地址,转换为*bool类型
       log.Println("sizeof:", unsafe.Sizeof(john), unsafe.Sizeof(john.name), unsafe.Sizeof(john.age), unsafe.Sizeof(john.gender))
       
       log.Println("Offset:")
       log.Println("name:", unsafe.Offsetof(john.name))
       log.Println("age:", unsafe.Offsetof(john.age))
       log.Println("gender:", unsafe.Offsetof(john.gender))
       log.Println("s:", unsafe.Offsetof(john.s))
       log.Println("sizeof s:", unsafe.Sizeof(john.s))
       
       log.Println(pp)
       log.Println("pp:", uintptr(pp))
       log.Println("name:", uintptr(pp) + unsafe.Offsetof(john.name))
       log.Println("age:", uintptr(pp) + unsafe.Offsetof(john.age))
       log.Println("gender:", uintptr(pp) + unsafe.Offsetof(john.gender))
       log.Println("get pointer value")
       log.Println("name:",(*string)(unsafe.Pointer(uintptr(pp) + unsafe.Offsetof(john.name))))
       log.Println("age:", (*int)(unsafe.Pointer(uintptr(pp) + unsafe.Offsetof(john.age))))
       log.Println("gender:", (*bool)(unsafe.Pointer(uintptr(pp) + unsafe.Offsetof(john.gender))))
      
       // 进行赋值
       *pname = "Alice"
       *page = 28
       *pgender = false
       fmt.Println(john) // {Alice 28 false}
     }
    

      

    --------------------------------------------------------------

    go语言基本类型

    类型名称 有无符号 占用位数
    int8 Yes 8
    int16 Yes 16
    int32 Yes 32
    int64 yes 64
    uint8 No 8
    uint16 No 16
    uint32 No 32
    uint64 No 64
    int Yes 32或64,等于cpu位数
    uint No 32或64,等于cpu位数
    rune No 32,与uint32等价
    byte No 8,与uint8等价
    uintptr No 64,与uint64等价

    rune 类型是 Unicode 字符类型,和 uint32 类型等价,通常用于表示一个 Unicode 字符。rune 和 uint32 可以互换使用。

    byte 类型与uint8类型等价,byte类型一般用于强调数值是一个原始的内存数据而不是 一个小的整数。

    uintptr 是一种无符号的整数类型,与uint64等价,范围:0 ~ 18446744073709551615,足以容纳任何指针值转换来的整型值。 uintptr类型只有在底层编程是才需要,特别是Go语言和C语言函数库或操作系统接口相交互的地方。GC 不把 uintptr 当指针,uintptr 无法持有对象。uintptr 类型的目标会被回收,所以不要声明uintptr类型的变量

    不管它们的具体大小,int、uint和uintptr是不同类型的兄弟类型。其中int和int32也是 不同的类型, 即使int的大小也是32bit,在需要将int当作int32类型的地方需要一个显式的类型转换操作,反之亦然

    unsafe.Pointer

    一个可以指向任意类型的指针,不可以进行数值计算
    有四种区别于其他类型的特殊操作:
    1. 任意类型的指针值均可转换为 Pointer
    2. Pointer 均可转换为任意类型的指针值
    3. uintptr 均可转换为 Pointer
    4. Pointer 均可转换为 uintptr

    unsafe 库让 golang 可以像C语言一样操作计算机内存,但这并不是golang推荐使用的,能不用尽量不用,就像它的名字所表达的一样,它绕过了golang的内存安全原则,是不安全的,容易使你的程序出现莫名其妙的问题,不利于程序的扩展与维护

    示例1(unsafe.Pointer用来取地址,uintptr用来取地址的10进制数值)

    package main
    import (
    	"fmt"
    	"unsafe"
    )
    func main() {
    	a := int(5)
    	p := &a
    	fmt.Println("p:", p)                                                     // a的地址,16进制形式
    	fmt.Println("unsafe.Pointer(&a):", unsafe.Pointer(&a))                   // a的地址,16进制形式
    	fmt.Println("uintptr(upackage main
    import (
    	"fmt"
    	"unsafe"
    )
    func main() {
    	a := int(5)
    	p := &a
    	fmt.Println("p:", p)                                                     // a的地址,16进制形式
    	fmt.Println("unsafe.Pointer(&a):", unsafe.Pointer(&a))                   // a的地址,16进制形式
    	fmt.Println("uintptr(unsafe.Pointer(&a)):", uintptr(unsafe.Pointer(&a))) // a的地址,10进制形式
    }
    nsafe.Pointer(&a)):", uintptr(unsafe.Pointer(&a))) // a的地址,10进制形式
    }
    

      

    PS:每次运行a的地址可能是不同的,但 unsafe.Pointer(&a) 和 uintptr(unsafe.Pointer(&a))的值是一样的,只是前者是指针类型,16进制,后者是整数类型,10进制,两者相等,这里 0xc00000a0b8,转换为10进制即 824633761976

    示例2(地址计算,偏移,并修改)

    package main
    import (
    	"fmt"
    	"unsafe"
    )
    func main() {
    	a := [4]int{0, 1, 2, 3}
    	p1 := unsafe.Pointer(&a[1]) // p1指向a[1]的起始地址
    	// a[1]的地址类型转换为uintptr类型,后移2个元素的位置,这样移动到a[3]的起始地址位置
    	p3 := unsafe.Pointer(uintptr(p1) + 2 * unsafe.Sizeof(a[0]))
    	*(*int)(p3) = 6 // 先将Pointer类型转换为*int类型,再赋值
    	fmt.Println("a =", a) // 打印出来结果是:a = [0 1 2 6]
    }
    

      

    S:unsafe.Pointer 是桥梁,可以让任意类型的指针实现相互转换,也可以将任意类型的指针转换为 uintptr 进行指针运算

    示例3(unsafe.Offsetof的使用)

    该函数返回属性x的起始地址和结构体起始地址的字节数

    func Offsetof(x ArbitraryType) uintptr

    package main
    import (
    	"fmt"
    	"unsafe"
    )
    type Person struct {
    	name   string
    	age    int
    	gender bool
    }
    func main() {
    	john := Person{"John", 30, true}
    	pp := unsafe.Pointer(&john) // 结构体的起始地址
    	pname := (*string)(unsafe.Pointer(uintptr(pp) + unsafe.Offsetof(john.name))) 	// 属性name的起始地址,转换为*string类型
    	page := (*int)(unsafe.Pointer(uintptr(pp) + unsafe.Offsetof(john.age)))		 	// 属性age的起始地址,转换为*int类型
    	pgender := (*bool)(unsafe.Pointer(uintptr(pp) + unsafe.Offsetof(john.gender)))	// 属性gender的起始地址,转换为*bool类型
     
    	// 进行赋值
    	*pname = "Alice"
    	*page = 28
    	*pgender = false
    	fmt.Println(john) // {Alice 28 false}
    }
    

      

  • 相关阅读:
    数据库与数据仓库的区别
    MySQL数据库与表的最基本命令大盘点
    SQL Server 2008创建数据库
    [HttpClient]简单使用GET请求
    [HttpClient]HttpClient简介
    [jQuery编程挑战]003 克隆一个页面元素及其相关事件
    [设计模式]观察者模式
    [jQuery编程挑战]002:实现一个转盘大抽奖
    [设计模式]备忘录模式
    [javascript]String添加trim和reverse方法
  • 原文地址:https://www.cnblogs.com/oxspirt/p/14676406.html
Copyright © 2011-2022 走看看