不能直接转换,因*int与*c.int类型不兼容;应通过int(cptr)读取或cptr=c.int(goval)写入,禁用unsafe.pointer强制转换。

Go 里怎么把 *C.int 转成 *int
不能直接类型断言或强制转换,Go 的 *int 和 *C.int 是完全不同的类型,底层内存布局虽相似,但 Go 运行时禁止跨语言指针混用——否则会 panic 或触发 GC 错误。
正确做法是先取值再赋值,或用 C.GoBytes/C.GoString 做安全拷贝(针对字符串/字节数组)。对单个整数:
- 如果只是读取:
val := int(*cPtr),其中cPtr是*C.int - 如果要写回 C 内存:
*cPtr = C.int(goVal),别碰*int指针 - 绝不要写
(*int)(unsafe.Pointer(cPtr))—— 这绕过类型检查,GC 可能回收底层内存,导致悬垂指针
C.CString 返回的指针必须手动 C.free
C.CString 在 C 堆上分配内存,Go 的 GC 完全不管理它。不调用 C.free 就是内存泄漏,且泄漏速度随调用频率线性增长。
常见错误场景:在循环里反复调用 C.CString 却只在函数末尾 free 一次,或 panic 后没执行 free。
立即学习“go语言免费学习笔记(深入)”;
- 务必配对使用:
cs := C.CString("hello"); defer C.free(unsafe.Pointer(cs)) - 如果传给 C 函数后,C 侧会长期持有该指针(比如注册回调),那就不能 defer free,得由 C 侧负责释放,并确保 Go 侧不再访问
-
C.CString返回的是*C.char,不是 Go 字符串;C.GoString才是从 C 指针构造 Go 字符串的安全方式
从 C 回调里传指针到 Go,为什么一解引用就 crash
典型错误是 C 回调函数里把局部变量地址(比如栈上数组)传给 Go,Go 收到后尝试读写,此时 C 栈帧已销毁,指针指向垃圾内存。
本质是生命周期错位:C 的栈变量生命只到函数返回,而 Go 侧可能在任意时刻访问。
- 解决方案只有两个:C 侧用
malloc分配堆内存,并约定由 Go 调用C.free;或 C 侧传数据副本(如通过struct成员值传递,而非指针) - 如果必须传指针,C 侧需确保该内存生命周期 ≥ Go 使用周期,例如全局变量、static 变量,或由 C 的资源管理器统一维护
- Go 侧收到指针后,别直接转成 Go 指针,优先用
C.GoBytes(ptr, size)拷一份到 Go 堆上再处理
CGO 中 unsafe.Pointer 转换的三个硬约束
所有涉及 unsafe.Pointer 的转换,都踩在 Go 类型系统和 GC 的边界上,三条规则漏一条就出问题:
- 目标类型大小必须一致:比如
C.int是 4 字节,对应 Go 的int32,不是int(在 64 位平台是 8 字节) - 原始指针必须指向有效、未被 GC 回收的内存:C 分配的堆内存 OK,C 栈变量 NO,Go 变量地址传给 C 后,若 Go 变量被 GC,C 再访问就 crash
- 转换后的 Go 指针不能逃逸到包级变量或长期存活结构体中——它只能在当前函数作用域内用完即弃,否则 GC 无法判断是否还在被引用
最常被忽略的是第三条:有人把转换后的 *int32 存进 map 或 channel,以为只是存个指针,其实已经让 Go 运行时失去对该内存的跟踪能力。










