首页 » 网站推广 » php指针应用技巧_golang2021数据格式91指针运用入门与unsafePointer

php指针应用技巧_golang2021数据格式91指针运用入门与unsafePointer

访客 2024-11-29 0

扫一扫用手机浏览

文章目录 [+]

注:变量值所在内存地址的值不即是该内存地址存储的变量值。

和 PHP、Python、Java 不同,Go 措辞支持指针,如果一个变量是指针类型的,那么就可以用这个变量来存储指针类型的值。

php指针应用技巧_golang2021数据格式91指针运用入门与unsafePointer

大略示例

php指针应用技巧_golang2021数据格式91指针运用入门与unsafePointer
(图片来自网络侵删)

我们来看一个大略的示例:

a := 100

var ptr int // 声明指针类型

ptr = &a // 初始化指针类型值为变量 a

fmt.Println(ptr)

fmt.Println(ptr)

上面代码中的 ptr 便是一个指针类型,表示指向存储 int 类型值的指针。
ptr 本身是一个内存地址值,以是须要通过内存地址进行赋值(通过 &a 可以获取变量 a 所在的内存地址),赋值之后,可以通过 ptr 获取指针指向内存地址存储的变量值(我们常日将这种引用称作「间接引用」),以是上述代码打印结果是:

0xc0000a2000

100

每次打印的 ptr 值可能不一样,由于存储变量 a 的内存地址在变动,不同操作系统打印的结果也不相同。

PHP/Java 中也有类似通过 & 进行引用传值的用法,实在这种用法的实质也是指针,只不过 PHP/Java 在措辞级别屏蔽了指针的观点而已。

Go 措辞之以是引入指针类型,紧张基于两点考虑,一个是为程序员供应操作变量对应内存数据构造的能力;另一个是为了提高程序的性能(指针可以直接指向某个变量值的内存地址,可以极大节省内存空间,操作效率也更高),这在系统编程、操作系统或者网络运用中是不容忽略的成分。

利用场景

指针在 Go 措辞中有两个范例的利用场景:

类型指针

切片

作为类型指针时,许可对这个指针类型数据指向的内存地址存储值进行修正,通报数据时如果利用指针则无须拷贝数据从而节省内存空间,此外和 C 措辞中的指针不同,Go 措辞中的类型指针不能进行偏移和运算,因此更为安全。

切片类型我们前面已经先容过,由指向数组起始元素的指针、元素数量和容量组成,以是切片与数组不同,是引用类型,而非值类型。

基本利用

下面我们以一个大略的示例代码来演示 Go 措辞中指针的基本利用。

指针类型的声明和初始化

指针变量在传值时之以是可以节省内存空间,是由于指针指向的内存地址的大小是固定的,在 32 位机器上占 4 个字节,在 64 位机器上占 8 个字节,这与指针指向内存地址存储的值类型无关。

关于指针类型的声明我们在开头已经演示过,这里我们再转头看下这段代码:

var ptr int

fmt.Println(ptr)

a := 100

ptr = &a

fmt.Println(ptr)

fmt.Println(ptr)

当指针被声明后,没有指向任何变量内存地址时,它的零值是 nil,然后我们可以通过在给定变量前加上取地址符 & 获取该变量对应的内存地址,再将其赋值给声明的指针类型,这样,就完成对指针类型的初始化了,接下来我们可以通过在指针类型前加上间接引用符 获取指针指向内存空间存储的变量值。

当然,和所有其他 Go 数据类型一样,我们也可以通过 := 对指针类型进行初始化:

a := 100

ptr := &a

fmt.Printf("%p\n", ptr)

fmt.Printf("%d\n", ptr)

底层会自动判断指针的类型,在格式化输出时,可以通过 %p 来标识指针类型。

此外,还可以通过内置函数 new 声明指针:

ptr := new(int)

ptr = 100

通过指针传值

我们再来看一个通过指针传值的示例,通过指针传值就类似于 PHP/Java 中通过引用传值,这样做的好处是节省内存空间,此外还可以在调用函数中实现对变量值的修正,由于直接修正的是指针指向内存地址上存储的变量值,而不是值拷贝。

为了表示出差异,我们先看不该用指针的值拷贝示例:

func swap(a, b int) {

a, b = b, a

fmt.Println(a, b)

}

func main() {

a := 1

b := 2

swap(a, b)

fmt.Println(a, b)

}

上述代码的打印结果是:

2 1

1 2

下面我们通过指针传值来重构上述代码:

func swap(a, b int) {

a, b = b, a

fmt.Println(a, b)

}

func main() {

a := 1

b := 2

swap(&a, &b)

fmt.Println(a, b)

}

上述代码的打印结果是

2 1

2 1

由于这次,我们是通过指针传值的(&a、&b 都是指针,只不过我们没有显示声明而已),直接会对内存地址存储变量值进行交流操作,而主函数中的 a、b 变量仅仅是对应内存存储空间的别名而已,以是调用完 swap 函数后,它们所对应的内存空间存储值已经交流过来了。

unsafe.Pointer

我们前面先容的指针都是被声明为指定类型的,而 unsafe.Pointer 是特殊定义的一种指针类型,它可以包含任意类型变量的地址(类似 C 措辞中的 void 类型指针)。
Go 官方文档对这个类型有如下四个描述:

任何类型的指针都可以被转化为 unsafe.Pointer;

unsafe.Pointer      可以被转化为任何类型的指针;

uintptr 可以被转化为 unsafe.Pointer;

unsafe.Pointer 可以被转化为 uintptr。

指针类型转化

因此,unsafe.Pointer 可以在不同的指针类型之间做转化,从而可以表示任意可寻址的指针类型:

i := 10

var p int = &i

var fp float32 = (float32)(unsafe.Pointer(p))

fp = fp 10

fmt.Println(i) // 100

这里,我们将指向 int 类型的指针转化为了 unsafe.Pointer 类型,再转化为 float32 类型(参考前面的 unsafe.Pointer 转化规则 1、2)并进走运算,末了创造 i 的值发生了改变。

这个示例解释了 unsafe.Pointer 是一个万能指针,可以在任何指针类型之间做转化,这就绕过了 Go 的类型安全机制,所以是不屈安的操作。

指针运算实现

此外,根据上面的转化规则 3、4,unsafe.Pointer 还可以与 uintptr 类型之间相互转化,为什么要单独列出这个类型呢?

uintptr 是 Go 内置的可用于存储指针的整型,而整型是可以进行数学运算的!
因此,将 unsafe.Pointer 转化为 uintptr 类型后,就可以让本不具备运算能力的指针具备了指针运算能力:

arr := [3]int{1, 2, 3}

ap := &arr

sp := (int)(unsafe.Pointer(uintptr(unsafe.Pointer(ap)) + unsafe.Sizeof(arr[0])))

sp += 3

fmt.Println(arr)

这里,我们将数组 arr 的内存地址赋值给指针 ap,然后通过 unsafe.Pointer 这个桥梁转化为 uintptr 类型,再加上数组元素偏移量(通过 unsafe.Sizeof 函数获取),就可以得到该数组第二个元素的内存地址,末了通过 unsafe.Pointer 将其转化为 int 类型指针赋值给 sp 指针,并进行修正,终极打印的结果是:

[1 5 3]

这样一来,就可以绕过 Go 指针的安全限定,实现对指针的动态偏移和打算了,这会导致纵然发生数组越界了,也不会报错,而是返回下一个内存地址存储的值,这就毁坏了内存安全限定,以是这也是不屈安的操作,我们在实际编码时要只管即便避免利用,必须利用的话也要非常谨慎。

标签:

相关文章

介绍直播新纪元,轻松进入直播的五大步骤

随着互联网技术的飞速发展,直播行业在我国逐渐崛起,越来越多的人选择通过直播这一新兴媒介展示自己、分享生活、传递价值。对于许多新手来...

网站推广 2025-01-03 阅读1 评论0

介绍相机美颜原理,科技与美学的完美结合

随着科技的发展,智能手机的摄像头功能日益强大,美颜相机成为了许多人拍照的首选。美颜相机不仅满足了人们对于美的追求,更在视觉上给人带...

网站推广 2025-01-03 阅读1 评论0

介绍磁铁的制造,科学与艺术的完美结合

磁铁,一种神秘的物质,自古以来就吸引了无数人的目光。它不仅具有独特的磁性,还能在工业、医疗、科研等领域发挥重要作用。磁铁是如何制造...

网站推广 2025-01-03 阅读1 评论0

介绍电瓶激活方法,让电池焕发新生

随着科技的不断发展,电动汽车逐渐成为人们出行的首选。而电瓶作为电动汽车的核心部件,其性能直接影响着车辆的续航里程和行驶体验。新购买...

网站推广 2025-01-03 阅读1 评论0