cgo内存管理

C.CString

类型转换伴随着内存拷贝的开销。C.CString在C的堆上使用malloc申请了空间。例如

package main

// #include <stdio.h>
// #include <stdlib.h>
//
// static void myprint(char* s) {
//   printf("%s\n", s);
// }
import "C"
import "unsafe"

func main() {
  cs := C.CString("Hello from stdio")
  C.myprint(cs)
  C.free(unsafe.Pointer(cs)) // 去除这行将发生内存泄漏
}

o代码中的cs变量在传递给c代码使用完成之后,需要调用C.free进行释放。

避免拷贝

假设我们可以确定在c模块中不会修改Go传入的内存,并且c函数调用结束之后,c模块不会再持有这块内存,我们出于性能考虑,想避免这种拷贝,可以这样做:

package main

// #include <stdio.h>
// #include <stdlib.h>
//
// static void myprint(char* s, int len) {
//   int i = 0;
//   for (; i < len; i++) {
//     printf("%c", *(s+i));
//   }
//   printf("\n");
// }
import "C"
import "unsafe"

type StringStruct struct {
  str unsafe.Pointer
  len int
}

func main() {
  str := "Hello from stdio"
  ss := (*StringStruct)(unsafe.Pointer(&str))
  c := (*C.char)(unsafe.Pointer(ss.str))
  C.myprint(c, C.int(len(str)))
}

总结

  • 以上两种方式都是把go中的string传给char*,区别是第一种方式多了c的内存分配和拷贝。
  • 第二种方式并不是官方推荐方式也有缺陷,例如给jna使用时,内存异常崩溃,因为go的内存布局和c不同导致的。
  • 推荐还是使用第一种,但是要释放资源,或者通过c的函数来转换一下实现。

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×