
本文详解如何使用 cgo 将 go 字符串转换并复制到 c 的 char * 内存空间,强调内存分配与释放的正确流程,避免常见内存泄漏或非法访问错误。
本文详解如何使用 cgo 将 go 字符串转换并复制到 c 的 char * 内存空间,强调内存分配与释放的正确流程,避免常见内存泄漏或非法访问错误。
在 Go 中通过 CGO 与 C 代码交互时,字符串传递是一个高频但易出错的操作。Go 字符串是只读、不可寻址的 UTF-8 字节序列,而 C 的 char * 是可修改、需手动管理生命周期的堆内存指针。不能直接赋值或通过 C.GoString() 反向写入(如 C.GoString(cstr) = str 是语法错误且语义无效),因为 C.GoString() 仅用于从 C 字符串创建 Go 字符串副本,且返回的是新分配的 Go 字符串,无法用于写入目标 C 内存。
正确做法是使用 C.CString() 函数:它接收一个 Go 字符串,将其内容(含终止空字节 \0)复制到 C 堆上,并返回 *C.char。该函数内部调用 malloc 分配内存,因此调用者必须显式释放,否则将导致内存泄漏。
以下是一个完整、安全的示例:
package main
/*
#include <stdlib.h>
*/
import "C"
import (
"unsafe"
)
func copyStringToC() *C.char {
str := "hello, cgo"
cstr := C.CString(str)
// 注意:此处 cstr 指向 C 堆内存,内容为 "hello, cgo\0"
return cstr
}
// 使用后务必释放
func freeCString(cstr *C.char) {
if cstr != nil {
C.free(unsafe.Pointer(cstr))
}
}
// 完整使用示例(含错误防范)
func example() {
cstr := copyStringToC()
defer freeCString(cstr) // 推荐用 defer 确保释放
// 此时可安全传给 C 函数,例如:
// C.some_c_function(cstr)
// 若需验证内容(仅调试用):
goStr := C.GoString(cstr)
println("Copied string:", goStr) // 输出:Copied string: hello, cgo
}⚠️ 重要注意事项:
- C.CString() 不会复用或写入已有 C 内存;它总是分配新内存。因此,若你已有一个 *C.char 指针(如由 C 函数分配或栈上变量),不能用 C.CString() 直接“复制到”它——你需要改用 C.memcpy 或手动逐字节拷贝(并确保目标缓冲区足够大且以 \0 结尾)。
- C.CString("") 会分配一个仅含 \0 的字节,始终安全。
- 不要对 C.CString() 返回的指针调用 C.free() 多次,也不要释放 nil 指针(尽管 C.free(nil) 是安全的,显式判空仍是良好实践)。
- 若字符串内容可能含 \0(非标准 C 字符串场景),C.CString() 会在首个 \0 处截断——此时应改用 C.CBytes() 并自行处理长度和空终止。
总结:Go → C 字符串传递的核心是「分配 + 释放」闭环。牢记 C.CString() 负责分配,C.free() 负责释放,二者必须成对出现;任何遗漏都将破坏程序稳定性与内存安全性。

















