zoukankan      html  css  js  c++  java
  • [go]指针

    一、三种指针类型

    1. 普通指针 *
    2. 非类型安全指针 unsafe.Pointer(类似c的void*)
    3. 内置类型指针 uintpter(其实就是一个整数,代表地址,支持运算)
    graph LR 普通指针-->unsafe.Pointer unsafe.Pointer-->普通指针 unsafe.Pointer-->uintptr uintptr-->unsafe.Pointer

    普通指针和unsafe.Pointer类型的指针都能代表引用一个地址,被GC发现。但是uintptr是不代表引用一个地址的。不能被GC发现,如果没有其他引用可能会被回收。




    二、普通指针 (64位系统8字节)

    • 不支持算术运算++ --
    • 不同类型指针不允许比较
    • 不同类型指针不允许相互赋值
    • 指针不允许强转其他类型

    作用:在函数传参时修改原值




    三、非类型安全指针 unsafe.Pointer()

    • 可以被转化为任意类型的指针

    如下,将 *int 类型指针转化为 *float 类型

    	var p1* int
    	var p2 unsafe.Pointer
    	var p3* float64
    	p2 = unsafe.Pointer(p1)
    	p3  = (*float64)(p2)
    	log.Println(reflect.TypeOf(p3))//*float64
    



    四、uintptr内置类型

    uintptr类型可以进行指针运算,uintptr是个整数,内存地址编号

    内存地址可以看做是一个线性的字节数组。按字节编址,存储单元(8位一个单元)都有一个编号,

    	name :="biningo"
    	namep:=unsafe.Pointer(&name)
    	namept:=uintptr(namep)
    	log.Printf("%x %x
    ",&name,namept) //c000054200 c000054200
    

    uintptr运算:

    	arr:=[5]int64{1,2,3,4,5}
    
    	uptr:=uintptr(unsafe.Pointer(&arr))
    	log.Printf("%x %x
    ",uptr,&arr[0])
    
    	uptr +=uintptr(8)
    	log.Printf("%x %x
    ",uptr,&arr[1])
    
    	arr2:=*(*int64)(unsafe.Pointer(uptr))
    	log.Println(arr2) //2
    	arr2=100
    	log.Println(arr[1]) //2 不变
    
    	arr3:=(*int64)(unsafe.Pointer(uptr))
    	*arr3 = 999
    	log.Println(arr[1]) //999 改变
    

    五、unsafe包的方法

    下面方法返回的都是uintptr类型,uintptr类型是个整数

    1. Alignof :查询字节对齐的大小,大部分是8字节,有些是4字节和1字节,有多种类型混合的话就按最大的字节对齐,

      比如下面,字节对齐对大的是8字节 ,所以都按8字节来对齐

    	type A struct {
    	n int32
    	s string
    	p *int
    	b bool
    	up uintptr
    }
    
    	a:=A{}
    	log.Println(unsafe.Alignof(a.n)) //4
    	log.Println(unsafe.Alignof(a.s)) //8
    	log.Println(unsafe.Alignof(a.p)) //
    	log.Println(unsafe.Alignof(a.b))//1
    	log.Println(unsafe.Alignof(a.up))//8
    
    
    	type S struct {
    
    	i1 int32 //对齐大小是 4
    	i2 int32 //
    }
    
    	s:=S{}
    	log.Println(unsafe.Offsetof(s.i1))//0 
    	log.Println(unsafe.Offsetof(s.i2))//4  这里就不是8开始了,因为都是4字节对齐
    
    1. Offsetof :返回偏移的大小
    	log.Println(unsafe.Offsetof(a.n)) //0
    	log.Println(unsafe.Offsetof(a.s)) //8
    	log.Println(unsafe.Offsetof(a.p)) //24 [+16 string的大小是16字节]
    	log.Println(unsafe.Offsetof(a.b)) //32
    	log.Println(unsafe.Offsetof(a.up)) //40
    
    1. Sizeof:返回类型的大小



    六、综合案例

    操作结构体

    下面展示指针堆结构体的操作,下面的操作会改变原来对象的值

    type T struct {
    	t1 byte
    	t2 int32
    	t3 int64
    	t4 string
    	t5 bool
    }
    //普通指针,用于传递对象地址,不能进行指针运算。
    //
    //unsafe.Pointer:通用指针类型,用于转换不同类型的指针,不能进行指针运算。
    //
    //uintptr:用于指针运算,GC 不把 uintptr 当指针,uintptr 无法持有对象。uintptr 类型的目标会被回收。
    
    func main() {
    
    	t := &T{1, 2, 3, "this is a example", true}
    	ptr := unsafe.Pointer(t)
    	t1 := (*byte)(ptr)
    	log.Println(*t1) //1
    
    	t2 := (*int32)(unsafe.Pointer(uintptr(ptr) + unsafe.Offsetof(t.t2)))
    	*t2 = 99
    	log.Println(t.t2) //99 被改变了
    
    	t3 := (*int64)(unsafe.Pointer(uintptr(ptr) + unsafe.Offsetof(t.t3)))
    	*t3 = 123
    	log.Println(t.t3) //123
    }
    

    操作slice

    下面看看go内置的数据类型slice

    type slice struct {
    	array unsafe.Pointer //底层数组的指针 长度是8
    	len   int //切片长度 int型size=8
    	cap   int //切片实际长度
    }
    
    
    int main(){
        s := make([]int, 9, 20)
    	var Len = *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + uintptr(8))) 
    	log.Println(Len, len(s)) // 9 9 长度是9
        var Cap = *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + uintptr(16))) //0+8+8
    	log.Println(Cap, cap(s)) // 20 20 cap是20
        
    }
    
    
  • 相关阅读:
    [C++11新特性] weak_ptr和unique_ptr
    [C++11新特性] shared_ptr共享的智能指针
    VS2019 Qt5.15.2 开发环境搭建
    【C++11 新特性】Lambda表达式(三)
    【C++11 新特性】bind(二)
    【C++11 新特性】function(一)
    【IPC 进程间通信】有名管道的简单实现
    【IPC 进程间通信】常用进程间通信方式总结
    Qt 文件常见操作管理类
    【GitHub 开源分享】QML 在线预览工具
  • 原文地址:https://www.cnblogs.com/biningooginind/p/12685406.html
Copyright © 2011-2022 走看看