在Go中调用返回字符串的C函数,无法获取C侧分配的字符串
问题分析与解决方案:Go调用C函数无法获取C侧分配的字符串
你遇到的问题不是Go垃圾回收器提前回收,核心原因是C语言中指针是值传递——你在C函数里修改的是传入指针的副本,根本没把分配好的内存地址传回Go侧!
具体问题拆解
1. my_reverse函数的问题
在C的my_reverse里,你给dst指针赋值了malloc的新地址,但这个dst是函数的形参(局部变量),修改它不会影响Go侧传入的c_dst指针。Go侧的c_dst还是你最初用C.CString(dst)创建的空字符串地址,自然读不到C侧反转后的内容。另外你最后用return string(*c_dst)也是错误的——*c_dst只取了指针指向的第一个字符,不是整个字符串。
2. some_text函数的问题
同理:C函数里给buffer赋值malloc的新地址,只是修改了形参的副本,Go侧的c_dst还是原来的空字符串地址,所以拿不到"year"。
解决方案:用双重指针传递内存地址
要让C侧分配的内存地址能传回Go,需要把Go侧的指针地址传给C(也就是传递指针的指针),这样C函数里就能直接修改Go侧指针指向的内存地址。
修改后的完整代码如下:
package main /* #include <string.h> #include <stdlib.h> #include <stdio.h> // 修改为双重指针,让C可以修改Go侧的指针指向 void my_reverse(char* src, int len, char **dst){ *dst = malloc(sizeof(char) * (len + 1)); printf("[c-part] src=%s\n", src); for (int i = 0; i < len; ++i) { (*dst)[i] = src[len - 1 - i]; } (*dst)[len] = 0; printf("[c-part] dst=%s\n", *dst); } // 同样修改为双重指针 void some_text(char** buffer, unsigned long long int *year){ *buffer = malloc(200 * sizeof(char)); sscanf("year 2018", "%s %16llu", *buffer, year); printf("will return (%s, %16llu)\n", *buffer, *year); } */ import "C" import "unsafe" import "fmt" func Reverse(a string) (dst string) { c_src := C.CString(a) defer C.free(unsafe.Pointer(c_src)) c_len := C.int(len(a)) // 声明C char指针,初始为NULL var c_dst *C.char // 传递指针的地址(双重指针)给C函数 C.my_reverse(c_src, c_len, &c_dst) // 必须在Go侧释放C分配的内存,避免泄漏 defer C.free(unsafe.Pointer(c_dst)) // 用C.GoString安全转换C字符串到Go字符串 return C.GoString(c_dst) } func Sometext() (dst string, year int64) { var c_dst *C.char c_year := C.ulonglong(0) // 传递指针的地址 C.some_text(&c_dst, &c_year) defer C.free(unsafe.Pointer(c_dst)) return C.GoString(c_dst), int64(c_year) } func main() { fmt.Printf("[gopart] dst=%v\n\n\n", Reverse("Hello World")) buf, year := Sometext() fmt.Printf("received (%v, %v)\n", buf, year) }
关键修改点说明
- C函数参数从
char* dst改为char** dst,允许直接修改传入指针的指向 - Go侧传递指针的地址(
&c_dst)给C函数,让C能修改Go侧的指针变量 - 使用
C.GoString(c_dst)完成C字符串到Go字符串的安全转换,替代手动转换 - 必须在Go侧调用
C.free释放C分配的内存:Go的GC不会管理C堆内存,遗漏会导致内存泄漏
运行结果
修改后运行,就能得到你期望的输出:
[c-part] src=Hello World [c-part] dst=dlroW olleH [gopart] dst=dlroW olleH will return (year, 2018) received (year, 2018)
内容的提问来源于stack exchange,提问作者user1392416
相关产品推荐
相关产品推荐

