
在 Go 的 cgo 中,C.CString 分配的内存位于 C 堆,必须显式调用 C.free 释放;若将其作为内联参数传入 C 函数(如 C.fn(C.CString("s"))),将无法在 Go 侧及时回收内存,极易导致内存泄漏或悬空指针。
在 go 的 cgo 中,`c.cstring` 分配的内存位于 c 堆,必须显式调用 `c.free` 释放;若将其作为内联参数传入 c 函数(如 `c.fn(c.cstring("s"))`),将无法在 go 侧及时回收内存,极易导致内存泄漏或悬空指针。
在使用 cgo 调用 C 函数时,C.CString(s) 会调用 C 标准库的 malloc 在 C 堆上分配一段以 \0 结尾的字符串内存,并返回 *C.char。该内存不会被 Go 运行时自动管理,必须由开发者手动调用 C.free(unsafe.Pointer(ptr)) 释放——否则将造成持续的内存泄漏。
⚠️ 危险写法(绝对避免):
// ❌ 内联调用:C.CString 返回值无变量绑定,无法获取指针以释放
ret := C.RandomCFunction(C.CString("foo"))此处 C.CString("foo") 的返回指针在函数调用后立即丢失,既无法在 Go 侧释放,也无法交由 C 函数安全接管(除非 C 函数明确声明“接管并负责释放”,但这是极少见且高风险的契约)。更严重的是,若后续该 C 函数被复用于已存在的 *C.char 变量,而内部又误执行 free,将引发重复释放(double-free)或访问已释放内存(use-after-free)。
✅ 推荐实践:显式声明、及时释放
应将 C.CString 结果赋值给局部变量,在调用 C 函数后立即释放(建议配合 defer 确保执行):
package main
/*
#include <stdlib.h>
#include <string.h>
// 示例 C 函数:仅读取字符串,不持有或释放
int RandomCFunction(const char* s) {
return strlen(s);
}
*/
import "C"
import (
"unsafe"
)
func callWithCString() int {
cstr := C.CString("foo") // 显式获取指针
defer C.free(unsafe.Pointer(cstr)) // 确保函数返回前释放
return int(C.RandomCFunction(cstr))
}? 关键注意事项:
- 必须
#include <stdlib.h></stdlib.h>才能使用C.free; -
C.free接收unsafe.Pointer,需显式转换unsafe.Pointer(cstr); - 若 C 函数需长期持有该字符串(如注册回调、缓存),则不应在 Go 侧立即释放,而应设计明确的所有权移交机制(例如提供配套的
DestroyC 函数),并在文档中严格约定; - 对于频繁调用场景,可考虑复用
*C.char缓冲区或使用C.CBytes+C.size_t配合长度传递,避免重复 malloc/free 开销。
总之,永远不要内联 C.CString —— 显式绑定、作用域清晰、释放可控,是保障 cgo 内存安全的黄金准则。

















