Golang中CGO如何处理void*指针

Golang中CGO如何处理void*指针 我有一个这样的C结构体:

typedef struct {
void* data;
} foo;

我想使用CGO将其复制到等效的Go结构体

type foo struct {
data unsafe.Pointer
}

但我遇到了panic:运行时错误:cgo参数包含指向未固定的Go指针的Go指针 我该怎么办?

4 回复

在将其传递给C之前,你是如何设置data字段的?

更多关于Golang中CGO如何处理void*指针的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


好的,pinner 中的 bug 将在下一个 Go 版本中修复。

此外,当我尝试使用 runtime.Pinner 固定 data 时,它提示 data 不是 Go 指针…

func main() {
    fmt.Println("hello world")
}

在CGO中处理void*指针时,遇到"未固定的Go指针"错误通常是因为Go的垃圾收集器移动了指针位置。你需要使用runtime.Pinner来固定指针,或者使用C.malloc分配C内存。以下是解决方案:

方法1:使用runtime.Pinner固定Go指针

package main

/*
#include <stdlib.h>

typedef struct {
    void* data;
} foo;
*/
import "C"
import (
    "runtime"
    "unsafe"
)

type Foo struct {
    data unsafe.Pointer
}

func main() {
    // 创建Go数据
    goData := []byte("Hello from Go")
    
    // 使用Pinner固定Go指针
    var pinner runtime.Pinner
    pinner.Pin(&goData[0])
    
    // 创建C结构体
    cFoo := C.foo{
        data: unsafe.Pointer(&goData[0]),
    }
    
    // 转换为Go结构体
    goFoo := Foo{
        data: unsafe.Pointer(cFoo.data),
    }
    
    // 使用数据...
    _ = goFoo
    
    // 确保在不再需要时解除固定
    pinner.Unpin()
}

方法2:使用C.malloc分配C内存

package main

/*
#include <stdlib.h>
#include <string.h>

typedef struct {
    void* data;
} foo;
*/
import "C"
import (
    "unsafe"
)

type Foo struct {
    data unsafe.Pointer
}

func main() {
    // Go数据
    goData := []byte("Hello from Go")
    
    // 使用C.malloc分配内存并复制数据
    cData := C.malloc(C.size_t(len(goData)))
    defer C.free(cData)
    
    C.memcpy(cData, unsafe.Pointer(&goData[0]), C.size_t(len(goData)))
    
    // 创建C结构体
    cFoo := C.foo{
        data: cData,
    }
    
    // 转换为Go结构体
    goFoo := Foo{
        data: unsafe.Pointer(cFoo.data),
    }
    
    // 访问数据
    // 注意:需要将unsafe.Pointer转换为正确的类型
    data := C.GoBytes(goFoo.data, C.int(len(goData)))
    _ = data
}

方法3:处理复杂场景的完整示例

package main

/*
#include <stdlib.h>
#include <string.h>

typedef struct {
    void* data;
    int size;
} buffer;

buffer* create_buffer(void* data, int size) {
    buffer* buf = (buffer*)malloc(sizeof(buffer));
    buf->data = malloc(size);
    memcpy(buf->data, data, size);
    buf->size = size;
    return buf;
}

void free_buffer(buffer* buf) {
    if (buf) {
        free(buf->data);
        free(buf);
    }
}
*/
import "C"
import (
    "fmt"
    "unsafe"
)

type Buffer struct {
    data unsafe.Pointer
    size int
}

func main() {
    // 准备数据
    goData := []byte("Hello, CGO!")
    
    // 创建C缓冲区
    cBuf := C.create_buffer(
        unsafe.Pointer(&goData[0]),
        C.int(len(goData)),
    )
    defer C.free_buffer(cBuf)
    
    // 转换为Go结构体
    goBuf := Buffer{
        data: unsafe.Pointer(cBuf.data),
        size: int(cBuf.size),
    }
    
    // 安全访问数据
    dataSlice := (*[1 << 30]byte)(goBuf.data)[:goBuf.size:goBuf.size]
    fmt.Printf("Data: %s\n", string(dataSlice))
    
    // 或者使用C.GoBytes
    goBytes := C.GoBytes(goBuf.data, C.int(goBuf.size))
    fmt.Printf("GoBytes: %s\n", string(goBytes))
}

关键注意事项:

  1. 不要直接传递Go指针到C,除非使用runtime.Pinner固定
  2. 使用C.malloc分配的内存由C管理,不受Go GC影响
  3. 使用C.GoBytes可以安全地将C数据复制到Go切片
  4. 确保释放C分配的内存,使用defer C.free()
  5. 在Go 1.21+中推荐使用runtime.Pinner,它比旧的runtime.KeepAlive更安全

选择哪种方法取决于你的具体需求:如果数据主要在Go端使用,使用方法1;如果需要在C端长期持有数据,使用方法2。

回到顶部