cgo笔记: 内存分配与对象转换 最近工作中需要使用部门中的c遗产,所以研究了一下cgo使用。体会就是,真香。 总结心得如下: 1. 盯死`malloc`和`free`。在c中的malloc在c中free,在go中malloc的在go中free。 2. 更方便的做法是,**仅使用c的结构和函数**,其它操作都使用go完成。好处是,C的代码会非常的简单。 3. 在强制类型转换时,一定要转对应类型。pointer指向什么,就转成什么。`**_Ctype_struct_FileInfo` 转换成 `[]*_Ctype_struct_FileInfo`,int32不要转成int64。 4. 和c有关的函数签名保持简单,直接使用c结构体,不要进行更多的类型转换。不要传unsafe.Pointer。 5. 不要跨包cgo,不要重复include。在包内闭环每个头文件的内容,对外用go函数包装c的操作。 示例:在go中创建c结构体,并调用c函数初始化它。 ```go /* #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中使用数组 ```go 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 } // 将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)[:ret] // flowTuples := unsafe.Slice((*C.dc_flow_log_info_t)(buf), ret) // 将buf转为go的slice,第二个参数为slice的长度,类型为 []C.dc_flow_log_info_t for i, item := range flowTuples { log.Printf("total:%v buf %v %v\n", ret, i, item.pkts) } // ... } ``` 示例:创建结构体,并且包装free函数 来自:https://www.reddit.com/r/golang/comments/iqy423/cgo_golang_free_allocated_memory_of_array_of_c/ ```go 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)) } ``` 来自 大脸猪 写于 2023-03-08 16:06 -- 更新于2023-03-22 10:37 -- 0 条评论