将结构体和结构体数组从Go语言传递给C函数。

huangapple go评论83阅读模式
英文:

Pass struct and array of structs to C function from Go

问题

遇到了这个问题。只能获取传递结构的第一个成员...我做错了什么?从Go语言传递结构的正确方法是什么?

这是一个不起作用的示例:

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}}

	// 错误的结果 = [25, 0]
	C.pass_struct((*C.Foo)(unsafe.Pointer(&foo)))

	// 完全不起作用,SIGSEGV
	// C.pass_array((**C.Foo)(unsafe.Pointer(&foos[0])))

	// 错误的结果 = [25, 0], [50, 0]
	out := make([]*C.Foo, len(foos))
	out[0] = (*C.Foo)(unsafe.Pointer(&foos[0]))
	out[1] = (*C.Foo)(unsafe.Pointer(&foos[1]))
	C.pass_array((**C.Foo)(unsafe.Pointer(&out[0])))
}
英文:

Stuck with this problem. Able to get only the first member of passed structure... What I do wrong? And what is the right way to pass the structure from Go to C?

This is my example of how it doesn't work:

package main

/*
#include &lt;stdio.h&gt;

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

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

    for(i = 0; i &lt; 2; i++) {
        fprintf(stderr, &quot;[%d, %d]&quot;, in[i]-&gt;a, in[i]-&gt;b);
    }
    fprintf(stderr, &quot;\n&quot;);
}

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

*/
import &quot;C&quot;

import (
	&quot;unsafe&quot;
)

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(&amp;foo)))

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

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

答案1

得分: 8

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

我猜测你在64位系统上运行。请注意,在Go语言中,int是64位的,但在C语言中很可能是32位的。

如果我将Foo的定义更改为以下内容,那么它在我的机器上可以工作(64位Linux):

type Foo struct {
    A int32
    B int32
}

然而,我认为这可能会引发麻烦。你可以让你的Go和C代码使用相同的结构体,如下所示:

type Foo _Ctype_Foo
英文:

The problem is that Foo and _Ctype_Foo are different structures.

I would guess you are running on 64 bit. Note that int is 64 bit in go, but is quite likely to be 32 bit in C.

If I change the definition of Foo to this then it works on my machine (64 bit linux)

type Foo struct {
    A int32
    B int32
}

However I would say that is a recipe for trouble - make your Go and C code use the same structure with

type Foo _Ctype_Foo

答案2

得分: 7

我知道这是一个相当旧的话题,但我偶然发现了它。这是一个修改过的(正确的)版本,其中包含了一些来自C语言对Go结构体的额外操作。

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
英文:

I know this is a fairly old topic but I stumbled across it. Here's a modified (correct) version with some additional manipulation from C land on the Go structs.

package main

/*
#include &lt;stdio.h&gt;

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

void pass_struct(Foo *in) { printf(&quot;%d : %d\n&quot;, in-&gt;a, in-&gt;b); }

void pass_array(Foo **in, int len) {
    for(int i = 0; i &lt; len; i++) {
	    pass_struct(in[i]);
	    in[i]-&gt;a += 1;
	    in[i]-&gt;b += 1;
    }
}
*/
import &quot;C&quot;

import (
    &quot;fmt&quot;
    &quot;unsafe&quot;
)

type Foo struct{ a, b int32 }

func main() {
    foo := Foo{10, 20}
    foos := []*Foo{&amp;Foo{1, 2}, &amp;Foo{3, 4}}

    fmt.Println(&quot;from C land&quot;)
    C.pass_struct((*C.Foo)(unsafe.Pointer(&amp;foo)))
    C.pass_array((**C.Foo)(unsafe.Pointer(&amp;foos[0])), C.int(len(foos)))
    fmt.Println(&quot;a &amp; b should have incremented with 1&quot;)

    fmt.Println(&quot;from Go land&quot;)
    for _, foo := range foos {
	    fmt.Printf(&quot;%d : %d\n&quot;, foo.a, foo.b)
    }
}

Output:

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

答案3

得分: 7

这两个答案现在都不适用(至少在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()
}
英文:

Neither of these answers work now (at least under Go 1.12). I wrote two another solutions:

package main

/*
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;

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

int pass_array(Foo **in) {
    int i;
	int r = 0;

    for(i = 0; i &lt; 2; i++) {
        r += in[i]-&gt;a;
        r *= in[i]-&gt;b;
    }
	return r;
}

*/
import &quot;C&quot;
import (
	&quot;fmt&quot;
	&quot;unsafe&quot;
)

type Foo struct {
	A int32
	B int32
}

func a() {
	foos := []Foo{{1, 2}, {3, 4}}

	l := len(foos)
	values := (*[1 &lt;&lt; 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(&amp;values[0])
	for i := 0; i &lt; l; i++ {
		C.free(unsafe.Pointer(values[i]))
	}
	C.free(unsafe.Pointer(values))
	fmt.Println(&quot;A finished&quot;, 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(&amp;values[0])
	for _, v := range values {
		C.free(unsafe.Pointer(v))
	}
	fmt.Println(&quot;B finished&quot;, val)
}

func main() {
	a()
	b()
}

huangapple
  • 本文由 发表于 2013年11月12日 00:00:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/19910647.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定