Golang uintptr 类型
最后修改时间 2025 年 5 月 8 日
本教程将介绍如何在 Go 中使用 `uintptr` 内置类型。我们将通过实际的内存地址操作示例,涵盖低级指针操作。
`uintptr` 是一种整数类型,其大小足以容纳任何指针的位模式。它主要用于与系统调用或 C 库交互时的不安全指针算术。
在 Go 中,`uintptr` 通常与 `unsafe` 包一起用于低级编程。它将内存地址表示为无符号整数,允许对指针进行算术运算。
基本的 uintptr 用法
此示例演示了如何将指针转换为 uintptr,然后再转换回来。它展示了指针和 uintptr 值之间的基本关系。
注意:这些操作需要 unsafe 包。
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 进行指针算术来访问结构体字段。它演示了计算字段偏移量。
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。
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 调用模式。
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 检查内存布局。它展示了如何检查切片的内部结构。
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 中的 `uintptr` 类型。