【问题标题】:Return strings from c function in Golang从Golang中的c函数返回字符串
【发布时间】:2018-06-22 19:12:42
【问题描述】:

我正在尝试在 GO 中调用 C 函数。这适用于某些扩展(对于整数)。不过也有问题 下面给出一个最小的例子

package main

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

void my_reverse(char* src, int len, char *dst){
  dst = malloc(sizeof(char) * (len + 1));
  printf("[c-part] src=%s\n", src);
  for (int i = 0; i < len; ++i)
  {
    dst[i] = src[len - 1 - i];
  }
  dst[len] = 0;
  printf("[c-part] dst=%s\n", dst);
}

void some_text(char* buffer, unsigned long long int *year){
  buffer = malloc(200 * sizeof(char));
  sscanf("year 2018d", "%s %16llu", buffer, year);
  printf("will return (%s, %16llu)\n", buffer, *year);

}
*/
import "C"

import "unsafe"
import "fmt"

func Reverse(a string) (dst string) {

    c_src := C.CString(a)
    defer C.free(unsafe.Pointer(c_src))
    c_len := C.int(len(a))
    c_dst := C.CString(dst)
    defer C.free(unsafe.Pointer(c_dst))

    C.my_reverse(c_src, c_len, c_dst)

    return string(*c_dst)

}

func Sometext() (dst string, year int64) {

    c_dst := C.CString("")
    c_year := C.ulonglong(0)
    defer C.free(unsafe.Pointer(c_dst))

    C.some_text(c_dst, &c_year)

    return string(*c_dst), int64(c_year)

}

func main() {
    fmt.Printf("[gopart] dst=%v\n\n\n", Reverse("Hello World"))

    buf, year := Sometext()
    fmt.Printf("received (%v, %v)\n", buf, year)
}

这是两个 c 函数,它们在 c 中分配一个新的缓冲区。 但是,我得到了输出

[c-part] src=Hello World
[c-part] dst=dlroW olleH
[gopart] dst=


will return (year,             2018)
received (, 2018)

这意味着,这些字符串在 C 中可用。但是我以后再也不能在 GO 中使用这些字符串了。 Go-Garbage 收集器会在我使用之前删除字符串吗?

我希望看到

[gopart] dst=dlroW olleH
received (year, 2018)

那里也是。

【问题讨论】:

  • 这是一个令人困惑的例子。如果您只想在 C 中再次分配,则不需要在 Go 中分配 C 字符串。您的输出参数还需要是指向您要替换的内容的指针,即 char** buffer.
  • 这对于char** 来说真的很奇怪。如何包装char * strcpy(char *dest, char *src); 之类的标准函数?我确信 c 函数具有正确的签名(不考虑 golang)。
  • 好吧,strcpy 不会分配自己,而您的函数会分配。如果您想这样工作,请事先分配正确的缓冲区大小并重用它。
  • 我明白了,你是对的!虽然,它没有回答这个问题。如果没有足够的缓冲区,应用程序就会崩溃,对吗?但我只是得到“”。
  • @leafbebop 不,不是这样。

标签: c string go cgo


【解决方案1】:

如果在 Go 中使用 C.String 初始化字符串,则不需要在 c 中处理内存分配

// Go string to C string // C 字符串在 C 堆中分配 使用malloc。 // 调用者有责任安排它 // 被释放,例如通过调用 C.free(确保包含 stdlib.h // 如果需要 C.free)。 func C.CString(string) *C.char

您可以使用 c.GoString() 将 C.String 转换回 go 字符串。下面是您的示例的工作代码 sn-p。

package main

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

void my_reverse(char* src, char *dst){
  printf("[c-part] src=%s\n", src);
  int n = strlen(src);
  for (int i = 0; i < n ; ++i)
  {
    dst[i] = src[n - 1 - i];
  }
  dst[n] = 0;
  printf("[c-part] dst=%s\n", dst);
}

void some_text(char* buffer, unsigned long long int *year){
  sscanf("year 2018d", "%s %16llu", buffer, year);
  printf("will return (%s, %16llu)\n", buffer, *year);
}
*/
import "C"

import "unsafe"
import "fmt"

func Reverse(a string) (dst string) {
    c_src := C.CString(a)
    defer C.free(unsafe.Pointer(c_src))
    c_dst := C.CString(dst)
    defer C.free(unsafe.Pointer(c_dst))
    C.my_reverse(c_src, c_dst)
    return C.GoString(c_dst)
}

func Sometext() (dst string, year int64) {
    c_dst := C.CString("")
    c_year := C.ulonglong(0)
    defer C.free(unsafe.Pointer(c_dst))
    C.some_text(c_dst, &c_year)
    return C.GoString(c_dst), int64(c_year)
}

func main() {
    fmt.Printf("[gopart] dst=%v\n\n\n", Reverse("Hello World"))

    buf, year := Sometext()
    fmt.Printf("received (%v, %v)\n", buf, year)
}

【讨论】:

  • 您的两个c_dst 都只分配一个空字符串(或单个空字节),然后将它们作为适当大小的缓冲区传递给导致缓冲区溢出的 c 函数。最好的情况是你的段错误,但更有可能是你默默地破坏了程序内存。
猜你喜欢
  • 2021-05-12
  • 1970-01-01
  • 1970-01-01
  • 2014-11-06
  • 2013-10-13
  • 1970-01-01
相关资源
最近更新 更多