Go禁止指针运算,仅支持取地址(&)和解引用(*),且有严格限制:不可对临时值、map元素、函数返回值等取址;解引用前须判空;指针不可比较或作map键;unsafe.Pointer应谨慎使用。
& 和 * 不是 C 那套玩法Go 明确禁止指针算术(pointer arithmetic),比如 p + 1、p++、uintptr(p) + size(除非你显式转成 uintptr 并绕过安全检查,但这是 unsafe 行为)。所以“指针运算”在 Go 中是个误导性说法——你真正能做的只有取地址和解引用。
常见错误现象:写 p++ 报错 invalid operation: p++ (non-numeric type *int);或尝试 &a[0] + 1 直接编译失败。
&x 只能用于可寻址的值(变量、结构体字段、切片元素等),不能用于字面量或函数调用结果(如 &fmt.Sprintf("x") 报错)*p 要求 p 是指针类型,且不为 nil,否则运行时 panic:panic: runtime error: invalid memory address or nil pointer dereference
nil),也不能作为 map key& 取地址的合法场景和陷阱取地址不是万能的。Go 编译器会拒绝给临时值或不可寻址对象取地址,这是为了防止悬垂指针。
使用场景:
func process(s *HeavyStruct)
func increment(x *int) { *x++ }
容易踩的坑:
&[]int{1,2,3} ❌ 编译报错;应先赋值再取址:arr := []int{1,2,3}; p := &arr&m["key"] ❌ 不合法(map 元素不可寻址);需先拷贝到局部变量:v := m["key"]; p := &v
&someFunc() ❌ 大概率报错;只有返回变量地址才安全,比如 &globalVar
* 解引用前必须确认非 nil,且类型匹配解引用是运行时行为,编译器不检查是否为 nil,全靠程序员防御。
典型错误现象:程序突然 crash,堆栈指向某行 *p,但没做空检查。
if p != nil { val := *p }*int 不能解引用为 *int64,哪怕底层都是整数;类型转换需显式:var p *int; q := (*int64)(unsafe.Pointer(p))(仅限
unsafe 场景)ps := &S{x: 1}; ps.x 等价于 (*ps).x,但不能写成 *ps.x(运算符优先级问题,实际是 *(ps.x))unsafe.Pointer?别轻易碰极少数场景绕不开:实现自定义内存池、与 C 交互、反射中获取结构体首地址、字节切片和字符串互转(如 string(unsafe.Slice(...)))。
但代价明确:
unsafe.Pointer 持有的内存,导致提前回收unsafe 的逻辑(官方不承诺兼容)替代方案优先考虑:reflect.SliceHeader / reflect.StringHeader(仍需 unsafe)、标准库的 bytes 和 strings 工具函数、或重构为纯 safe 代码。
真正的难点不在语法,而在理解 Go 的内存模型边界——它用限制换安全,而开发者得学会在边界内把事情做对。