uintptr是能存储地址的无符号整数,不参与gc管理,必须与unsafe.pointer在同一表达式中即时互转,否则易致内存失效、panic或脏读。

uintptr 本质是整数,不是指针
它只是个能存地址的无符号整数类型,uintptr 本身不参与 Go 的内存管理——GC 不会追踪它,编译器也不会检查它的生命周期。这意味着你把它当指针用,纯靠自己负责;一不小心,指向的内存就被回收了,后续读写就是未定义行为。
常见错误现象:panic: runtime error: invalid memory address or nil pointer dereference,或者读到脏数据、程序静默崩溃。
- 别用
uintptr长期保存地址,尤其不能跨函数返回或存在全局变量里 - 只在「立刻用」的上下文中转:比如调
syscall、操作unsafe.Pointer、做地址偏移计算 - 每次从
unsafe.Pointer转成uintptr后,必须紧接着再转回unsafe.Pointer才能安全解引用
和 unsafe.Pointer 互转的唯一安全写法
Go 官方文档强调:只有 uintptr 是由 unsafe.Pointer 直接转换而来,并且**在同一表达式中立刻转回**,才被编译器视为“可追踪”。一旦中间插入函数调用、赋值给变量、或跨语句,就失去保障。
正确示例:
立即学习“go语言免费学习笔记(深入)”;
Dbsite企业网站管理系统V1.5.0 秉承"大道至简 邦达天下"的设计理念,以灵巧、简单的架构模式构建本管理系统。可根据需求可配置多种类型数据库(当前压缩包支持Access).系统是对多年企业网站设计经验的总结。特别适合于中小型企业网站建设使用。压缩包内包含通用企业网站模板一套,可以用来了解系统标签和设计网站使用。QQ技术交流群:115197646 系统特点:1.数据与页
ptr := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&x)) + unsafe.Offsetof(x.field)))
错误示例(危险!):
u := uintptr(unsafe.Pointer(&x))<br>ptr := (*int)(unsafe.Pointer(u + unsafe.Offsetof(x.field)))
- 第一行赋值给变量
u后,&x的存活状态不再与u绑定,GC 可能在第二行执行前回收x - 调用任何函数(包括
fmt.Println)都可能触发栈重调度,导致中间变量失效 -
unsafe.Pointer→uintptr→unsafe.Pointer必须写在一条表达式里,或至少保证无函数调用、无变量中转
为什么不能直接用 uintptr 做指针运算
因为 uintptr 是整数,加减后还是整数,它不会自动变成合法指针。Go 禁止对 uintptr 解引用,你必须显式转回 unsafe.Pointer 才能构造指针类型。
使用场景集中在系统调用、内存布局操作、反射底层字段访问等极少数地方。日常业务代码几乎不需要碰。
- 字段偏移计算必须用
unsafe.Offsetof,不能靠uintptr硬算结构体布局(结构体可能有填充字节) - 数组切片底层数组地址偏移,要用
unsafe.Slice(Go 1.17+)或reflect.SliceHeader+unsafe.Pointer,而不是手动加uintptr - 32 位系统下
uintptr是 4 字节,64 位是 8 字节,硬编码地址常量会导致跨平台失败
容易被忽略的 GC 干扰点
最隐蔽的问题不是你忘了转回 unsafe.Pointer,而是你忘了「保持原对象存活」。哪怕写法完全正确,如果原始变量早于后续解引用就离开作用域,照样崩。
- 传入函数的参数如果是局部变量地址,要确保调用者维持其生命周期足够长(比如传指针而非值)
- 在 goroutine 中异步使用时,必须显式用
runtime.KeepAlive告诉编译器:“这个变量我还用得着” - 用
unsafe.Pointer指向堆上对象没问题,但若指向栈上局部变量,该 goroutine 切换或函数返回后栈帧消失,地址立即失效
真正难的从来不是怎么写那几行转换代码,而是判断「此刻这个地址背后的东西,还在不在内存里」。









