【问题标题】:Use Go slice in C在 C 中使用 Go 切片
【发布时间】:2019-01-02 16:26:15
【问题描述】:

我正在尝试使用返回切片的函数构建一个 go 共享库。
如何使用 C 代码中的切片?

package main

import "C"

type T struct {
    A C.int
    B *C.char
}

//export Test
func Test() []T {
    arr := make([]T, 0)
    arr = append(arr, T{C.int(1), C.CString("a")})
    arr = append(arr, T{C.int(2), C.CString("abc")})
    return arr
}

func main() {}

go build -o lib.so -buildmode=c-shared main.go

我现在有一个lib.so 和一个lib.h

打印数组值的 C 代码是什么?

#include <stdio.h>
#include "lib.h"

typedef struct {
  int   A;
  char* B;
} T;

int main() {
  GoSlice a = Test();
  for (int i = 0; i < 2; i++){
    printf("%s\n", ((T *)a.data)[i].B);
  }
}

gcc -o main main.c ./lib.so

【问题讨论】:

  • 不要将 Go 切片返回给 C 代码,因为 C 代码不知道 Go 切片是什么。给它一个数组指针,就像在普通 C 中一样。
  • 我尝试了很多不同的东西。我想不明白。我知道我不应该返回一个切片,我可能应该返回一个 unsafe.Pointer 或类似的东西。但我认为其他人会更容易理解我试图用这个例子做什么。如果您有任何示例,我将不胜感激:)

标签: c go cgo


【解决方案1】:

首先,C 不知道 Go 切片类型,也不知道如何操作它,因此您应该像在 C 中一样返回一个数组指针。

你也不能在 Go 中分配切片然后返回一个指向已分配内存的指针,而没有某种方法可以防止它被 GC'ed。在此示例中,仅在 C 中分配数组可能更简单。

p := C.malloc(2 * C.size_t(unsafe.Sizeof(T{})))

// convert the C pointer to a slice of `[]T` for convenient indexing
arr := unsafe.Slice((*T)(p), 2)
arr[0].A = C.int(1)
arr[0].B = C.CString("a")
arr[1].A = C.int(2)
arr[1].B = C.CString("abc")

您当然还需要处理数组大小,您可以像处理字符串一样将其组合成一个结构体,或者接受第二个指针参数来设置返回大小。

不要忘记,由于您在 C 中分配内存,因此您也有责任释放它。这适用于malloc 调用以及C.CString 数组。

【讨论】:

  • 为什么是(*[1 &lt;&lt; 30]*T)(p)[:2:2] 而不是(*[1 &lt;&lt; 30]T)(p)[:2:2]?那不是给出一片指针而不是一片结构吗?
  • @Hermann:是的,这是一片指针。此示例和原始示例都是人为的示例,应进行调整以修复正在使用的实际数据类型。
  • 显然,我很难理解 go 中的赋值语义。通过给出的示例,我希望切片中填充指针。这些结构似乎是在 go 中分配的,并且容易被垃圾收集。我怀疑这是我在调整示例时需要考虑的问题。对吗?
  • @Hermann:我想你已经明白了,这是一个很好的观点,结构是 Go 值。这是很久以前的事了,但我可能匆忙写了这个例子来突出C.malloc,而没有考虑到其余部分(或者我可能是从其他地方复制的?)。我会将其更新为不分配 go 的内容,并使用较新的 unsafe.Slice
【解决方案2】:

在 C 中使用 Go 切片


这是一个 C 打印 Go byte 切片的示例:

package main

/*
#include <stdio.h>

void printbuf(size_t len, unsigned char *buf) {
    printf("%lu [", len);
    if (!buf) {
        len = 0;
    }
    size_t maxwidth = 16;
    size_t width = len <= maxwidth ? len : maxwidth;
    for (size_t i = 0; i < width; i++) {
        if (i > 0) {
            printf(" ");
        }
        printf("%02X", buf[i]);
    }
    if (width < len) {
        printf(" ...");
    }
    printf("]\n");
}
*/
import "C"

func cbuf(buf []byte) (size C.size_t, ptr *C.uchar) {
    var bufptr *byte
    if cap(buf) > 0 {
        bufptr = &(buf[:1][0])
    }
    return C.size_t(len(buf)), (*C.uchar)(bufptr)
}

func main() {
    buf := make([]byte, 24, 32)
    for i := range buf {
        buf[i] = byte(i)
    }
    bufsize, bufptr := cbuf(buf)
    C.printbuf(bufsize, bufptr)
}

输出:

24 [00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ...]

func Test() []T 对 C 有错误的返回值。返回一个长度和一个数组指针,例如 cbuf

【讨论】:

  • 很高兴代码可以用go run编译,但是如果你将C代码分开在它自己的文件中,然后你编译共享库......你会得到一个运行时错误@987654327 @.
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-09
  • 2013-09-23
  • 1970-01-01
相关资源
最近更新 更多