最近工作中需要使用部门中的c遗产,所以研究了一下cgo使用。体会就是,真香。
总结心得如下:
- 在go中,可以调用
C.calloc
或者C.malloc
分配内存。两者的区别是calloc会填0初始化。 - 分配内存中要注意,在c调用的
calloc
,则在c中free
;在go调用的C.calloc
,使用goC.free
。这样就不容易出问题。 - 更方便的做法是,仅使用c的结构和函数,其它操作都使用go完成。好处是,C的代码会非常的简单。
- 在强制类型转换时,一定要对应类型。比如pointer指向什么,就转成什么。
**_Ctype_struct_FileInfo
转换成[]*_Ctype_struct_FileInfo
,int32不要转成int64。 - helper函数签名保持简单,不要进行更多的类型转换。例如,Free函数传入c结构体
air *C.struct_Result
,而不是其它需要转换的类型。 - 不要跨包cgo,不支持。在包内闭环,外部public接口使用go的签名。
示例:分配c结构体指针,并使用c函数初始化它。
代码语言:javascript复制/*
#include "shm.h"
*/
import "C"
import (
"log"
"unsafe"
)
func main(){
var reporter *C.report_agent_shm_t = (*C.report_agent_shm_t)(
C.calloc(1, C.sizeof_report_agent_shm_t)) // report_agent_shm_t 定义在shm.h中, 注意 sizeof_report_agent_shm_t,无须使用unsafe.SizeOf
defer C.free(unsafe.Pointer(reporter)) // 在go中C.calloc,则在go中C.free
var ret C.int = C.shm_open(reporter) // 此时调用c代码中的函数初始化这个对象,shm_open定义在shm.h中
if ret < 0 {
log.Fatalf("open nfc shm failed:%v", ret)
}
// report已完成初始化,使用 reporter.read_index 可直接取用字段
}
示例:在go中创建一段内存,并调用C函数读取数组到内存,最终在go中使用数组
代码语言:javascript复制 n := C.ulong(C.DEFAULT_BUCKET_CAPACITY * reporter.one_flow_size)
buf := C.malloc(n)
defer C.free(buf) // 在go中malloc,则在go中free
for{
C.memset(buf, 0, n)
ret := C.shm_dequeue(reporter, buf) // 调用c函数将内存填充为数组数据,ret是数组实际长度
if ret == 0 {
time.Sleep(time.Millisecond * 10)
continue
}
flowTuples := unsafe.Slice((*C.dc_flow_log_info_t)(buf), ret) // 将buf转为go的slice,第二个参数为slice的长度,类型为 []C.dc_flow_log_info_t
// 或者,将buf转换为go数组,类型为 *[DEFAULT_BUCKET_CAPACITY]_Ctype_struct_flow_log_info,这里要注意实际的长度ret可能小于DEFAULT_BUCKET_CAPACITY
// flowTuples := (*[C.DEFAULT_BUCKET_CAPACITY]C.dc_flow_log_info_t)(buf)
for i, item := range flowTuples {
log.Printf("total:%v buf %v %vn", ret, i, item.pkts)
}
// ...
}
示例:创建结构体,并且包装free函数
来自:https://www.reddit.com/r/golang/comments/iqy423/cgo_golang_free_allocated_memory_of_array_of_c/
代码语言:javascript复制package main
/*
#include "stdlib.h"
#include "stdint.h"
#include "stdio.h"
typedef struct FileInfo{
char *Name;
char *Path;
}FileInfo;
typedef struct Result{
FileInfo **files;
}Result;
*/
import "C"
import "unsafe"
func main() {
for {
var aiList []*C.struct_FileInfo
aif := (*C.struct_FileInfo)(C.malloc(C.sizeof_struct_FileInfo))
aif.Name = C.CString("abcd")
aif.Path = C.CString("/path")
aiList = append(aiList, aif)
air := (*C.struct_Result)(C.malloc(C.sizeof_struct_Result))
air.files = &aiList[0]
FreeMemory(air, len(aiList))
}
}
func FreeMemory(air *C.struct_Result, totalFiles int) {
files := (*[1 << 30]*C.struct_FileInfo)(unsafe.Pointer(air.files))[:totalFiles]
for _, item := range files {
C.free(unsafe.Pointer(item.Name))
C.free(unsafe.Pointer(item.Path))
C.free(unsafe.Pointer(item))
}
C.free(unsafe.Pointer(air))
}