ZetCode

Golang uintptr 类型

最后修改时间 2025 年 5 月 8 日

本教程将介绍如何在 Go 中使用 `uintptr` 内置类型。我们将通过实际的内存地址操作示例,涵盖低级指针操作。

`uintptr` 是一种整数类型,其大小足以容纳任何指针的位模式。它主要用于与系统调用或 C 库交互时的不安全指针算术。

在 Go 中,`uintptr` 通常与 `unsafe` 包一起用于低级编程。它将内存地址表示为无符号整数,允许对指针进行算术运算。

基本的 uintptr 用法

此示例演示了如何将指针转换为 uintptr,然后再转换回来。它展示了指针和 uintptr 值之间的基本关系。
注意:这些操作需要 unsafe 包。

basic_uintptr.go
package main

import (
    "fmt"
    "unsafe"
)

func main() {

    var x int = 42
    ptr := &x
    
    // Convert pointer to uintptr
    addr := uintptr(unsafe.Pointer(ptr))
    
    fmt.Printf("Pointer: %p\n", ptr)
    fmt.Printf("Address as uintptr: 0x%x\n", addr)
    
    // Convert back to pointer
    newPtr := (*int)(unsafe.Pointer(addr))
    fmt.Println("Value through new pointer:", *newPtr)
}

该代码安全地将指针转换为 uintptr,然后再转换回来。uintptr 以整数值的形式保存内存地址,可以打印或操作。

使用 uintptr 进行指针算术

此示例演示了如何使用 uintptr 进行指针算术来访问结构体字段。它演示了计算字段偏移量。

pointer_arithmetic.go
package main

import (
    "fmt"
    "unsafe"
)

type Person struct {
    name string
    age  int
}

func main() {

    p := Person{"Alice", 30}
    base := unsafe.Pointer(&p)
    
    // Calculate name field address
    namePtr := (*string)(unsafe.Pointer(base))
    fmt.Println("Name:", *namePtr)
    
    // Calculate age field address
    agePtr := (*int)(unsafe.Pointer(uintptr(base) + unsafe.Offsetof(p.age)))
    fmt.Println("Age:", *agePtr)
}

该代码使用 uintptr 算术直接访问结构体字段。`unsafe.Offsetof` 函数有助于安全地计算字段偏移量。

uintptr 和 unsafe.Pointer 之间的转换

此示例演示了 uintptr 和 unsafe.Pointer 之间的正确转换模式。它展示了在单个表达式中使用临时 uintptr。

conversion_patterns.go
package main

import (
    "fmt"
    "unsafe"
)

func main() {

    data := []byte{'G', 'o', 'l', 'a', 'n', 'g'}
    
    // Safe conversion pattern
    firstChar := *(*byte)(unsafe.Pointer(
        uintptr(unsafe.Pointer(&data[0]))))
    
    // Unsafe pattern (don't do this)
    // addr := uintptr(unsafe.Pointer(&data[0]))
    // ... potential GC here ...
    // firstChar := *(*byte)(unsafe.Pointer(addr))
    
    fmt.Printf("First character: %c\n", firstChar)
}

安全模式将转换保留在单个表达式中。如果发生垃圾回收,将 uintptr 存储在变量中可能会导致问题。

使用 uintptr 进行系统调用

此示例展示了 uintptr 在系统调用中的用法,其中必须将内存地址作为整数传递。它演示了 Windows API 调用模式。

syscall_example.go
package main

import (
    "fmt"
    "syscall"
    "unsafe"
)

func main() {

    user32 := syscall.NewLazyDLL("user32.dll")
    msgBox := user32.NewProc("MessageBoxW")
    
    text, _ := syscall.UTF16PtrFromString("Hello from Go!")
    caption, _ := syscall.UTF16PtrFromString("uintptr Example")
    
    // Convert pointers to uintptr for syscall
    ret, _, _ := msgBox.Call(
        0,
        uintptr(unsafe.Pointer(text)),
        uintptr(unsafe.Pointer(caption)),
        0)
    
    fmt.Printf("MessageBox returned: %d\n", ret)
}

syscall 需要 uintptr 参数。该代码将字符串指针转换为 uintptr 值以供 Windows API 调用。这是 FFI 中的常见模式。

使用 uintptr 进行内存检查

此示例演示了如何使用 uintptr 检查内存布局。它展示了如何检查切片的内部结构。

memory_inspection.go
package main

import (
    "fmt"
    "unsafe"
)

func main() {

    s := []int{10, 20, 30}
    
    // Get slice header
    header := (*[3]uintptr)(unsafe.Pointer(&s))
    
    // Extract slice components
    ptr := header[0]
    len := header[1]
    cap := header[2]
    
    fmt.Printf("Pointer: 0x%x\n", ptr)
    fmt.Printf("Length: %d\n", len)
    fmt.Printf("Capacity: %d\n", cap)
    
    // Access first element
    first := *(*int)(unsafe.Pointer(ptr))
    fmt.Println("First element:", first)
}

该代码使用 uintptr 检查切片头结构。这揭示了存储在切片中的底层数组指针、长度和容量值。

来源

Go 语言规范

本教程通过低级内存操作和系统交互的实际示例,涵盖了 Go 中的 `uintptr` 类型。

作者

我叫 Jan Bodnar,是一名充满热情的程序员,拥有丰富的编程经验。我从 2007 年开始撰写编程文章。迄今为止,我已撰写了 1400 多篇文章和 8 本电子书。我在编程教学方面拥有十多年的经验。

列出所有 Golang 教程