cgo笔记: 内存分配与对象转换

2023-03-14 17:38:15 浏览数 (2)

最近工作中需要使用部门中的c遗产,所以研究了一下cgo使用。体会就是,真香。

总结心得如下:

  1. 在go中,可以调用C.calloc或者C.malloc分配内存。两者的区别是calloc会填0初始化。
  2. 分配内存中要注意,在c调用的calloc,则在c中free;在go调用的C.calloc,使用goC.free。这样就不容易出问题。
  3. 更方便的做法是,仅使用c的结构和函数,其它操作都使用go完成。好处是,C的代码会非常的简单。
  4. 在强制类型转换时,一定要对应类型。比如pointer指向什么,就转成什么。**_Ctype_struct_FileInfo 转换成 []*_Ctype_struct_FileInfo,int32不要转成int64。
  5. helper函数签名保持简单,不要进行更多的类型转换。例如,Free函数传入c结构体air *C.struct_Result,而不是其它需要转换的类型。
  6. 不要跨包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))
}

0 人点赞