当前位置: 首页> 娱乐> 明星 > 【Go系列】Go的指针

【Go系列】Go的指针

时间:2025/7/12 15:53:48来源:https://blog.csdn.net/u013379032/article/details/140428934 浏览次数:0次

承上启下

        我们在前面的文章中,首先介绍了GO的基础语法,然后介绍了Goroutine和channel这个最具有特色的东西,同时介绍了Sync和context,以及在上篇文章中详细距离说明了Go里面用于高并发的多种写法。基础的使用方法也告一段落了,我们要进入新的篇章,就是Go的指针,这边的指针类型不仅是一个地址,还有Unsafe.Point,还有intptr,让我们详细看看。

开始学习

普通指针

在Go语言中,指针是一种特殊类型的变量,它存储了另一个变量的内存地址。指针在Go中虽然不像C或C++那样普遍使用,但它们在某些情况下仍然非常有用,尤其是在需要修改函数内部变量的值、避免大对象复制、实现数据结构(如链表、树等)时。

以下是关于Go语言中指针的详细介绍:

指针的基本概念

  • 内存地址:每个变量在内存中都有一个地址,指针变量存储的就是这个地址。
  • 解引用:通过指针访问它所指向的变量的值称为解引用。

声明指针

在Go中,指针的声明方式是在变量类型前加上*

var pointer *int

这里,pointer是一个指向int类型变量的指针。

初始化指针

指针必须在使用前进行初始化。你可以使用&操作符来获取一个变量的地址,并将其赋值给指针:

value := 10
pointer := &value

在这个例子中,pointer存储了变量value的内存地址。

解引用指针

使用*操作符可以解引用指针,访问或修改它所指向的值:

*pointer = 20

这将把变量value的值修改为20。

函数中的指针

在函数中传递指针允许你修改函数外部的变量:

func modifyValue(ptr *int) {*ptr = 30
}func main() {value := 10modifyValue(&value)fmt.Println(value) // 输出 30
}

在这个例子中,modifyValue函数通过指针参数修改了外部变量value的值。

指针的nil值

一个未初始化的指针有一个nil值,表示它不指向任何地址:

var pointer *int
if pointer == nil {fmt.Println("Pointer is nil")
}

指针和结构体

指针常用于结构体,可以创建结构体的指针,并通过指针访问或修改结构体的字段:

type Person struct {Name stringAge  int
}func main() {person := &Person{Name: "Alice", Age: 30}person.Age = 31 // 通过指针修改结构体的字段
}

指针数组与数组指针

  • 指针数组:一个数组,其元素是指针。

    var ptrArray [3]*int
    
  • 数组指针:一个指向数组的指针。

    var array [3]int
    var ptrToArray *[3]int = &array
    

指针的指针

虽然不常见,但你可以在Go中创建指向指针的指针:

var value int = 100
var ptr *int = &value
var ptrToPtr **int = &ptr

这里,ptrToPtr是一个指向ptr指针的指针。

注意事项

  • Go不支持指针算术,即你不能对指针进行加减操作。
  • Go的垃圾回收机制会自动管理内存,因此通常不需要手动释放指针指向的内存。

Unsafe.Point

在Go语言中,unsafe.Pointer 是一个特殊类型的指针,它可以指向任意类型的值。unsafe 包提供了一些绕过Go类型系统的功能,允许程序进行一些原本不被允许的操作,比如在不同指针类型之间进行转换,或者计算一个对象的实际内存大小等。

以下是关于 unsafe.Pointer 的一些关键点:

类型转换

unsafe.Pointer 可以用于在任意指针类型之间进行转换。例如,如果你有一个 *int 类型的指针,你可以将其转换为 unsafe.Pointer,然后再转换回其他类型的指针。

package mainimport ("fmt""unsafe"
)func main() {i := 42ip := &i         // *intptr := unsafe.Pointer(ip) // 转换为 unsafe.Pointeruptr := uintptr(ptr)     // 转换为 uintptr// 反向转换ptr = unsafe.Pointer(uptr) // 转换回 unsafe.Pointerip2 := (*int)(ptr)        // 转换回 *int*ip2 = 84fmt.Println(i) // 输出 84
}

访问任意内存地址

通过 unsafe.Pointer,你可以访问任意内存地址,这在Go的常规操作中是不被允许的,因为它绕过了Go的类型系统和内存安全检查。

ptr := unsafe.Pointer(uintptr(0x12345678))

上述代码试图访问一个特定的内存地址,这在实际的程序中是非常危险的,因为它可能导致未定义行为,包括程序崩溃。

计算结构体大小

unsafe.Sizeof 函数可以返回一个值的大小,单位是字节。这个函数通常与 unsafe.Pointer 一起使用来计算结构体的大小。

type MyStruct struct {a intb string
}s := MyStruct{a: 1, b: "hello"}
size := unsafe.Sizeof(s)
fmt.Println(size) // 输出结构体 MyStruct 的大小

使用 uintptr 进行指针算术

虽然Go不支持 unsafe.Pointer 的算术操作,但你可以将 unsafe.Pointer 转换为 uintptr,然后在 uintptr 上执行算术操作,最后再转换回 unsafe.Pointer

ptr := unsafe.Pointer(&s)
uptr := uintptr(ptr)
newPtr := unsafe.Pointer(uptr + unsafe.Offsetof(s.b)) // 访问结构体中的 b 字段

注意事项

  • 使用 unsafe 包绕过Go的类型系统和内存安全机制,需要非常小心,因为错误的使用可能会导致程序崩溃或者安全漏洞。
  • unsafe.Pointer 的使用应该限制在必要的范围内,并且要确保操作的安全性。
  • unsafe 包的内容可能会在不同的Go版本之间发生变化,因此在使用时应保持谨慎。

由于 unsafe 包的功能非常强大,Go官方建议开发者只有在没有其他选择的情况下才使用它,并且要确保代码的稳定性和安全性。

UintPtr

在Go语言中,intptr 并不是一个内置的类型。你可能在提到 uintptr 时出现了误解,或者是在引用其他语言中的类型。在Go语言中,与指针操作相关的类型是 uintptr

uintptr 类型

uintptr 是 unsafe 包中的一个类型,它足够大,可以存储任何类型的指针的位模式(即内存地址)。uintptr 类型主要用于低级编程,比如与操作系统接口、内存操作等。

以下是一些关于 uintptr 的关键点:

  • uintptr 是一个无符号整数类型,其大小足以容纳任何指针的位模式。
  • 它可以用于将指针转换为整数,反之亦然。
  • uintptr 可以用于执行指针算术,但这样做需要非常小心,因为它可能会绕过Go的内存安全保证。

示例

以下是如何使用 uintptr 的示例:

package mainimport ("fmt""unsafe"
)func main() {i := 42ptr := &i         // *intuptr := uintptr(unsafe.Pointer(ptr)) // 转换为 uintptr// 使用 uintptr 进行指针算术newPtr := unsafe.Pointer(uptr + unsafe.Sizeof(i))// 反向转换回指针类型newIntPtr := (*int)(newPtr)*newIntPtr = 84fmt.Println(i) // 输出 84
}

在这个例子中,我们首先将一个 *int 类型的指针转换为 uintptr,然后执行了指针算术操作(虽然在这个特定的例子中这样做没有意义,因为它只是在一个整数大小的范围内移动),最后将结果转换回 *int 类型的指针。

注意事项

  • 使用 uintptr 需要非常小心,因为不正确的使用可能会导致内存安全问题,比如访问未分配的内存、越界访问等。
  • uintptr 类型的值不应该被存储或以任何方式保留,因为它们可能会在垃圾回收期间变得无效。
  • 通常情况下,Go程序员不需要直接使用 uintptr,除非他们正在编写需要直接与操作系统或硬件交互的底层代码。

三种指针类型对比

在Go语言中,指针、unsafe.Pointer 和 uintptr 是三种不同的概念,它们在内存操作和类型转换中扮演着不同的角色。下面是它们的区别:

指针(如 *int

  • 定义:指针是一种变量,它存储了另一个变量的内存地址。
  • 用途:用于引用和修改变量,或者在函数调用中传递变量的地址以修改其值。
  • 类型安全:指针是类型安全的,它们只能指向特定类型的变量。
  • 示例
    var a int = 42
    var ptr *int = &a
    *ptr = 100 // 修改a的值
    

unsafe.Pointer

  • 定义unsafe.Pointer 是 unsafe 包中的一个特殊类型,它可以指向任意类型的变量。
  • 用途:用于在不同指针类型之间进行转换,或者在需要时进行底层的内存操作。
  • 类型安全unsafe.Pointer 本身不是类型安全的,因为它可以指向任何类型的变量,但它需要与其他类型安全的指针一起使用。
  • 示例
    var a int = 42
    var ptr unsafe.Pointer = unsafe.Pointer(&a)
    

uintptr

  • 定义uintptr 是 unsafe 包中的一个无符号整数类型,其大小足以存储任何类型的指针的位模式。
  • 用途:用于执行指针算术操作,或者将指针转换为整数以便进行低级内存操作。
  • 类型安全uintptr 不是类型安全的,因为它可以存储任何指针的位模式,并且可以用于执行指针算术,这可能会绕过Go的内存安全保证。
  • 示例
    var a int = 42
    var ptr uintptr = uintptr(unsafe.Pointer(&a))
    

区别

  • 类型安全

    • 指针是类型安全的,只能用于指向特定类型的变量。
    • unsafe.Pointer 不是类型安全的,可以指向任何类型的变量,但它需要与其他类型安全的指针一起使用。
    • uintptr 也不是类型安全的,它可以存储任何指针的位模式,并且可以用于执行指针算术。
  • 用途

    • 指针主要用于变量引用和修改变量的值。
    • unsafe.Pointer 用于在不同指针类型之间进行转换,或者在需要时进行底层的内存操作。
    • uintptr 用于执行指针算术操作,或者将指针转换为整数以便进行低级内存操作。
  • 内存安全

    • 使用指针时,Go的垃圾回收器会确保指向的变量在需要时不会被回收。
    • 使用 unsafe.Pointer 和 uintptr 时,程序员需要确保操作不会导致内存安全问题,比如越界访问或访问未分配的内存。

总结来说,指针是Go中用于日常变量引用和修改变量的类型安全工具,而 unsafe.Pointer 和 uintptr 用于更底层的内存操作,它们提供了更大的灵活性和能力,但同时也带来了更高的风险,因为它们不是类型安全的,并且需要程序员更加小心地使用。

关键字:【Go系列】Go的指针

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: