当前位置: 移动技术网 > IT编程>脚本编程>Go语言 > [go]指针

[go]指针

2020年04月13日  | 移动技术网IT编程  | 我要评论

一、三种指针类型

  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\n",&name,namept) //c000054200 c000054200

uintptr运算:

	arr:=[5]int64{1,2,3,4,5}

	uptr:=uintptr(unsafe.pointer(&arr))
	log.printf("%x %x\n",uptr,&arr[0])

	uptr +=uintptr(8)
	log.printf("%x %x\n",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
    
}

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网