cgo和某些字符串切片的运行时错误

问题描述 投票:0回答:1

我已经删除了我遇到的问题,同时包含一些C代码以使用swig使用golang,但问题不在于swig。

我可以传入一个基本的字符串切片但是只要我用基本字符串以外的任何东西构造切片,我就会感到恐慌:运行时错误:cgo参数有Go指针指向Go指针。

go version go1.8.5 linux/amd64

这是示例代码及其输出

package main

import (
    "fmt"
    "reflect"
    "unsafe"
)

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

typedef struct { char *p; int n; } _gostring_;
typedef struct { void* array; int len; int cap; } _goslice_;

void prtText(char * const *txt, int len)
{
    int i = 0;

    for ( i=0; i<len; i++ ) {
        printf("Text %d is: %s\n", i, txt[i]);
    }
}

void _wrap_printText(_goslice_ _swig_go_0) {

  _gostring_ *p;

  char **arg1 = (char **)calloc(_swig_go_0.len, sizeof(char*));
  if (arg1) {
    for (int i=0; i<_swig_go_0.len; i++) {
      p = &(((_gostring_*)_swig_go_0.array)[i]);
      arg1[i] = calloc(1,(p->n)+1);
      strncpy(arg1[i], p->p, p->n);
    }
  }
  int arg2 = _swig_go_0.len;

  prtText((char *const *)arg1,arg2);
}

*/
import "C"

func PrintText(arg1 []string) {
    C._wrap_printText(*(*C._goslice_)(unsafe.Pointer(&arg1)))
}

func main() {
    s := []string{}

    s = append(s, "blah")
    s = append(s, "hello")
    s = append(s, "again")

    ns := []string{}

    ns = append(ns, "ns: "+s[0])
    ns = append(ns, "ns: "+s[1])
    ns = append(ns, "ns: "+s[2])

    fmt.Println("type s:", reflect.TypeOf(s))
    fmt.Println("type ns:", reflect.TypeOf(ns))
    fmt.Println("s:", s)
    fmt.Println("ns:", ns)

    PrintText(s)
    PrintText(ns)
}


go build -i -x -gcflags '-N -l' main.go

./main
type s: []string
type ns: []string
s: [blah hello again]
ns: [ns: blah ns: hello ns: again]
Text 0 is: blah
Text 1 is: hello
Text 2 is: again
panic: runtime error: cgo argument has Go pointer to Go pointer

正如您所看到的,第一个字符串切片工作正常,但只要我执行除基本字符串以外的任何操作,它就会失败。我已经尝试先将新字符串附加到切片之前,但问题仍然存在。

我究竟做错了什么?

go cgo
1个回答
1
投票

你基本上是通过原始的Go指针。相反,你应该自己构建C数组。

作为一般规则,在任何地方看到unsafe应该让你怀疑。围绕cgo问题很少是正确的方法。

使用Passing array of string as parameter from go to C function中的帮助程序并在代码中使用它们:

package main

import (
  "fmt"
  "reflect"
)

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

void prtText(char * const *txt, int len)
{
    int i = 0;

    for ( i=0; i<len; i++ ) {
        printf("Text %d is: %s\n", i, txt[i]);
    }
}

static char**makeCharArray(int size) {
        return calloc(sizeof(char*), size);
}

static void setArrayString(char **a, char *s, int n) {
        a[n] = s;
}

static void freeCharArray(char **a, int size) {
        int i;
        for (i = 0; i < size; i++)
                free(a[i]);
        free(a);
}

*/
import "C"

func main() {
  s := []string{}

  s = append(s, "blah")
  s = append(s, "hello")
  s = append(s, "again")

  ns := []string{}

  ns = append(ns, "ns: "+s[0])
  ns = append(ns, "ns: "+s[1])
  ns = append(ns, "ns: "+s[2])

  fmt.Println("type s:", reflect.TypeOf(s))
  fmt.Println("type ns:", reflect.TypeOf(ns))
  fmt.Println("s:", s)
  fmt.Println("ns:", ns)

  sargs := C.makeCharArray(C.int(len(s)))
  defer C.freeCharArray(sargs, C.int(len(s)))
  for i, p := range s {
    C.setArrayString(sargs, C.CString(p), C.int(i))
  }

  nsargs := C.makeCharArray(C.int(len(ns)))
  defer C.freeCharArray(nsargs, C.int(len(ns)))
  for i, p := range ns {
    C.setArrayString(nsargs, C.CString(p), C.int(i))
  }

  C.prtText(sargs, C.int(len(s)))
  C.prtText(nsargs, C.int(len(ns)))
}

输出现在符合预期:

$ ./main 
type s: []string
type ns: []string
s: [blah hello again]
ns: [ns: blah ns: hello ns: again]
Text 0 is: blah
Text 1 is: hello
Text 2 is: again
Text 0 is: ns: blah
Text 1 is: ns: hello
Text 2 is: ns: again
© www.soinside.com 2019 - 2024. All rights reserved.