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

如何将任意Go变量传入接受void*类型参数的C函数?

结论先行

不能直接将interface{}类型参数转换为unsafe.Pointer传给C的set函数,会导致C侧拿到错误的内存地址。interface{}在Go底层是包含类型指针、值指针的二元结构体,直接取interface{}变量的地址传递的是这个结构体本身的地址,而非你要传入的Go变量的实际地址。

正确实现方案

需要结合reflect反射包获取interface{}中存储的实际值指针,再传递给C函数,完整实现如下:

1. CGO基础引入配置

首先在Go代码中通过注释引入C的set函数,导入必要的依赖包:

/*
void set(void *data)
{
    // do some work with data (which points to a Go variable)
}
*/
import "C"
import (
    "errors"
    "reflect"
    "runtime"
    "unsafe"
)

2. 实现Go包装函数

func InvokeSet(data interface{}) error {
    // 校验入参必须为指针类型,否则无法获取有效原始地址
    val := reflect.ValueOf(data)
    if val.Kind() != reflect.Ptr {
        return errors.New("入参必须为指针类型,避免值拷贝导致C侧修改不生效")
    }
    // 取出interface中存储的实际值指针
    rawPtr := unsafe.Pointer(val.Pointer())
    // 调用C侧set函数
    C.set(rawPtr)
    // 保证Go变量在C函数执行完成前不会被GC回收
    runtime.KeepAlive(data)
    return nil
}

注意事项

  • 如果C侧需要长期持有传入的指针,不能仅依赖runtime.KeepAlive,需要在Go侧主动持有该变量的引用直到C侧不再使用,避免GC回收后C侧访问野指针崩溃。
  • 如果要传递Go字符串、切片等特殊类型给C侧,不要直接传变量指针:传字符串底层数组可使用unsafe.Pointer(unsafe.StringData(str)),传切片底层数组可使用unsafe.Pointer(&slice[0]),同时要把长度也同步传给C侧。
  • 不要传递Go栈上分配的变量指针给C侧长期使用,Go栈动态扩容时会移动栈变量地址,导致C侧指针失效。

内容的提问来源于stack exchange,提问作者user7698505

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 16:57:03