go 语言禁止直接访问内存地址和指针算术,仅支持安全的 & 取地址与 * 解引用操作;unsafe 包虽可绕过限制但属未定义行为,业务代码应避免使用。

Go 中无法直接访问内存地址
Go 语言不支持指针算术(pointer arithmetic),也没有类似 C 的 *((int*)0x12345678) 这种通过硬编码地址读写内存的操作。所有指针都必须由 Go 运行时合法分配或取址得到,且不能进行加减、强制类型转换为整数后再解引用等行为——这类操作在标准 Go 中是非法的,会触发编译错误或 panic。
你真正能做的,是获取变量的地址、传递地址、解引用已知合法指针。所谓“内存地址访问”,在 Go 里仅限于这层安全抽象。
& 和 * 是唯一可用的地址操作符
Go 只提供两个与地址直接相关的操作:& 取地址,* 解引用。它们只对变量或字段有效,且类型严格匹配:
-
&x返回*T类型指针,其中x必须是可寻址的(如变量、结构体字段、切片元素),不能是常量、字面量或函数返回值(除非是可寻址的复合字面量字段) -
*p要求p是合法的*T类型指针;若p == nil,运行时 panic:invalid memory address or nil pointer dereference - 指针类型不可相互转换,
*int不能转成*float64,哪怕底层都是 8 字节——这是类型系统强制的安全边界
示例:
var x int = 42 p := &x // ✅ 合法:取变量地址 y := *p // ✅ 合法:解引用 z := *&x // ✅ 等价于 x,&x 是 *int,*(&x) 回到 int // q := &42 // ❌ 编译错误:cannot take the address of 42 // r := (*int)(unsafe.Pointer(&x)) // ❌ 非法类型转换,需 unsafe 包且仍不推荐
想绕过限制?unsafe 是唯一入口,但代价极高
只有导入 unsafe 包,并配合 uintptr 和 unsafe.Pointer,才可能模拟低级内存操作。但这属于未定义行为(undefined behavior)范畴,极易导致崩溃、数据损坏或 GC 错误:
-
unsafe.Pointer是可转换为任意指针类型的桥梁,但它本身不能解引用 - 把指针转成
uintptr后,若该地址对应的对象被 GC 回收,再转回指针并解引用 → crash - 用
unsafe.Offsetof计算结构体字段偏移是安全的;但用uintptr(unsafe.Pointer(&s)) + offset手动跳转并解引用 → 高风险 - 标准库中仅少数地方(如
sync/atomic、reflect底层)谨慎使用,业务代码应完全避免
反模式示例(不要复制):
import "unsafe"
var s struct{ a, b int }
p := unsafe.Pointer(&s)
aPtr := (*int)(unsafe.Pointer(uintptr(p) + unsafe.Offsetof(s.a))) // ⚠️ 危险:依赖内存布局,且无 GC 保护
*aPtr = 100 // 可能成功,也可能在下次 GC 后 segfault
真正该关注的:指针在接口、方法和逃逸分析中的表现
日常开发中,指针的“内存意义”其实体现在三个更实际的层面:
- 方法集:只有
*T类型才有权实现接收者为*T的方法;传值调用T无法触发*T方法,容易误以为“没生效” - 逃逸分析:
&x若逃逸到堆,会导致额外分配;用go tool compile -gcflags="-m" main.go可观察 - 接口动态调度:当把
*T赋给接口时,接口底层存储的是指针值;而T值则存储副本——影响性能和是否反映修改
比如:
type Counter struct{ n int }
func (c *Counter) Inc() { c.n++ }
var c Counter
var i interface{} = &c // ✅ 能调用 Inc()
// var i interface{} = c // ❌ c 是值,没有 *Counter 方法集
Go 的指针设计刻意屏蔽了裸地址操作,把注意力引向值语义、所有权和运行时安全。一旦开始琢磨“怎么读 0x7ffeabcd 处的字节”,说明已经偏离 Go 的使用范式——这时候该检查是不是选错了语言,或者需求本身是否真需要绕过内存安全。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











