You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:04:40