【问题标题】:Pass struct and array of structs to C function from Go将结构和结构数组从 Go 传递给 C 函数
【发布时间】:2019-05-15 19:13:31
【问题描述】:

卡住了这个问题。只能获得传递结构的第一个成员......我做错了什么?将结构从 Go 传递到 C 的正确方法是什么?

这是我的例子,说明它是如何不起作用的:

package main

/*
#include <stdio.h>

typedef struct {
    int a;
    int b;
} Foo;

void pass_array(Foo **in) {
    int i;

    for(i = 0; i < 2; i++) {
        fprintf(stderr, "[%d, %d]", in[i]->a, in[i]->b);
    }
    fprintf(stderr, "\n");
}

void pass_struct(Foo *in) {
    fprintf(stderr, "[%d, %d]\n", in->a, in->b);
}

*/
import "C"

import (
    "unsafe"
)

type Foo struct {
    A int
    B int
}

func main() {

    foo := Foo{25, 26}
    foos := []Foo{{25, 26}, {50, 51}}

    // wrong result = [25, 0]
    C.pass_struct((*_Ctype_Foo)(unsafe.Pointer(&foo)))

    // doesn't work at all, SIGSEGV
    // C.pass_array((**_Ctype_Foo)(unsafe.Pointer(&foos[0])))

    // wrong result = [25, 0], [50, 0]
    out := make([]*_Ctype_Foo, len(foos))
    out[0] = (*_Ctype_Foo)(unsafe.Pointer(&foos[0]))
    out[1] = (*_Ctype_Foo)(unsafe.Pointer(&foos[1]))
    C.pass_array((**_Ctype_Foo)(unsafe.Pointer(&out[0])))
}

【问题讨论】:

    标签: go cgo


    【解决方案1】:

    问题在于Foo_Ctype_Foo 是不同的结构。

    我猜你是在 64 位上运行的。请注意,int 在 go 中是 64 位的,但在 C 中很可能是 32 位。

    如果我将 Foo 的定义更改为这个,那么它可以在我的机器上运行(64 位 linux)

    type Foo struct {
        A int32
        B int32
    }
    

    但是我会说这是一个麻烦的秘诀 - 让你的 Go 和 C 代码使用相同的结构

    type Foo _Ctype_Foo
    

    【讨论】:

    • 同意你关于明确使用 C 类型的结论。只是说明一下,golang的int不一定是64bit的; 至少 32位:golang.org/pkg/builtin/#int
    【解决方案2】:

    我知道这是一个相当古老的话题,但我偶然发现了它。这是一个修改过的(正确的)版本,在 Go 结构上使用 C 语言进行了一些额外的操作。

    package main
    
    /*
    #include <stdio.h>
    
    typedef struct {
        int a;
        int b;
    } Foo;
    
    void pass_struct(Foo *in) { printf("%d : %d\n", in->a, in->b); }
    
    void pass_array(Foo **in, int len) {
        for(int i = 0; i < len; i++) {
            pass_struct(in[i]);
            in[i]->a += 1;
            in[i]->b += 1;
        }
    }
    */
    import "C"
    
    import (
        "fmt"
        "unsafe"
    )
    
    type Foo struct{ a, b int32 }
    
    func main() {
        foo := Foo{10, 20}
        foos := []*Foo{&Foo{1, 2}, &Foo{3, 4}}
    
        fmt.Println("from C land")
        C.pass_struct((*C.Foo)(unsafe.Pointer(&foo)))
        C.pass_array((**C.Foo)(unsafe.Pointer(&foos[0])), C.int(len(foos)))
        fmt.Println("a & b should have incremented with 1")
    
        fmt.Println("from Go land")
        for _, foo := range foos {
            fmt.Printf("%d : %d\n", foo.a, foo.b)
        }
    }
    

    输出:

    from C land
    10 : 20
    1 : 2
    3 : 4
    a & b should have incremented with 1
    from Go land
    2 : 3
    4 : 5
    

    【讨论】:

    • 我收到此错误信息panic: runtime error: cgo argument has Go pointer to Go pointer
    【解决方案3】:

    这些答案现在都不起作用(至少在 Go 1.12 下)。我写了另外两个解决方案:

    package main
    
    /*
    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct {
        int a;
        int b;
    } Foo;
    
    int pass_array(Foo **in) {
        int i;
        int r = 0;
    
        for(i = 0; i < 2; i++) {
            r += in[i]->a;
            r *= in[i]->b;
        }
        return r;
    }
    
    */
    import "C"
    import (
        "fmt"
        "unsafe"
    )
    
    type Foo struct {
        A int32
        B int32
    }
    
    func a() {
        foos := []Foo{{1, 2}, {3, 4}}
    
        l := len(foos)
        values := (*[1 << 28]*C.Foo)(C.malloc(C.size_t(C.sizeof_Foo * l)))
        for i, f := range foos {
            foo := (*C.Foo)(C.malloc(C.size_t(C.sizeof_Foo)))
            (*foo).a = C.int(f.A)
            (*foo).b = C.int(f.B)
            values[i] = foo
        }
        val := C.pass_array(&values[0])
        for i := 0; i < l; i++ {
            C.free(unsafe.Pointer(values[i]))
        }
        C.free(unsafe.Pointer(values))
        fmt.Println("A finished", val)
    }
    
    func b() {
        foos := []Foo{{5, 6}, {7, 8}}
    
        values := make([]*C.Foo, len(foos))
        for i, f := range foos {
            p := (*C.Foo)(C.malloc(C.size_t(C.sizeof_Foo)))
            values[i] = p
            (*p).a = C.int(f.A)
            (*p).b = C.int(f.B)
        }
        val := C.pass_array(&values[0])
        for _, v := range values {
            C.free(unsafe.Pointer(v))
        }
        fmt.Println("B finished", val)
    }
    
    func main() {
        a()
        b()
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-09-13
      • 2011-04-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-24
      相关资源
      最近更新 更多