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` 类型。