c.cstring返回的*c.char不能“用完就扔”,因其在c堆上malloc分配,go gc无法回收,必须配对调用c.free,否则内存永久泄漏。

Go字符串传给C后,如果没手动管理内存,C侧用着用着就变空指针或乱码——根本原因是C.CString分配的内存没配对释放,或者误把Go栈/堆变量地址直接传给了C长期持有。
为什么C.CString返回的*C.char不能“用完就扔”
C.CString(s string) 在C堆上malloc一块内存,复制s内容并加\0,返回*C.char。这块内存C.free不调,就永远不回收;Go的GC也管不了它。常见错误是只在函数开头调C.CString,但忘了defer C.free,尤其在多分支、error early return场景下极易漏掉。
- 若C函数只是临时读取(如
C.printf),必须确保C.free在C函数返回后立刻执行,不能等到函数末尾才free——因为C可能异步回调或缓存指针 - 若C函数会保存该指针(如注册日志回调),绝不能用
C.CString,而应改用C.CBytes+ 手动生命周期控制,或让C侧负责释放 - 对格式化字符串(如
C.printf第一个参数),即使用了C.CString,GCC仍报-Wformat-security警告;此时应改用const char*类型别名绕过检查,而非硬扛警告
什么时候绝对不能传 &goString[0] 或 &x
Go字符串底层是struct { data *byte; len int },&goString[0]拿到的是指向底层字节数组首地址的*byte,但它不是C风格的char*(缺
Go字符串底层是struct { data *byte; len int },&goString[0]拿到的是指向底层字节数组首地址的*byte,但它不是C风格的char*(缺\0结尾),且该数组由Go GC管理。一旦Go代码失去对该字符串的引用,GC可能回收内存,C再访问就是悬空指针。
- 禁止将
&s[0](s为string或[]byte)直接转*C.char传给C长期使用 - 禁止把局部变量地址(如
var x int; &x)传给C回调函数——函数返回后变量栈帧销毁,C再调就崩 - 若C需长期持有字符串数据,必须用
C.CString或C.CBytes复制到C堆,并明确约定谁释放:Go释放就配对C.free,C释放就让C侧提供free_fn
[]string → char** 转换中容易漏掉的三处释放
将[]string转char**时,要建两层C内存:C.CString每字符串一份,再用C.malloc或unsafe.Slice建char*数组。漏任何一层都会泄漏。
- 每个
C.CString(s)都要配一个C.free,不能只free数组头 - 存放
*C.char指针的数组本身如果是C.malloc分配的,也要C.free;如果是Go切片[]*C.char,则只需free其中每个元素,数组本身由Go管理 - 若C函数接收
char**后自行strdup或malloc副本,Go侧仍要负责原始*C.char的释放,否则双重分配+零释放
最易被忽略的是:C函数文档没写清是否“take ownership”,导致Go侧不敢free,C侧又不free,结果整块C堆内存永久泄漏。动手前务必确认C库的内存契约——是“borrow”还是“steal”。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











