工作中遇到的Go语言调用C函数的场景比较多,之前也写过一篇《cgo中将C函数返回的数组转为Go中的slice》。

目前在开发OpenSIPS的过程中,有些功能用C写起来麻烦,故第一次尝试了用C调用Go。

首先用Go实现功能,示例代码如下

package mainimport "C"import ("fmt""sync""time"
)var notifyChan chan struct{} = make(chan struct{})
var wg sync.WaitGroup//export Test
func Test(strs []string) string {for _, v := range strs {fmt.Println(v)}for i := 0; i < 3; i++ {wg.Add(1)go func(idx int) {defer func() {fmt.Printf("work %d cleaning\n",idx)wg.Done()}()for {select {case <-notifyChan:fmt.Printf("work %d should exit\n",idx)returndefault:}fmt.Printf("work %d working\n",idx)time.Sleep(time.Second)}}(i)}return "OK"
}//export Close
func Close() {close(notifyChan)wg.Wait()
}//export Test2
func Test2(input map[string]string) map[string]string {return nil
}func main() {}

用命令生成动态库和头文件:

go build -o libfunc.so -buildmode=c-shared func.go

有几个注意点:

1. 必须是package main并且包含main函数,否则go build会报错

2. main函数里即使有代码段,也不会执行,所以为空即可

3. "import C" 不要忘记,否则虽然能生成动态库,但里面不会有导出符号,头文件也不会生成

4. export和"//"之间不能有空格

生成的头文件:

/* Code generated by cmd/cgo; DO NOT EDIT. *//* package command-line-arguments */#line 1 "cgo-builtin-export-prolog"#include <stddef.h> /* for ptrdiff_t below */#ifndef GO_CGO_EXPORT_PROLOGUE_H
#define GO_CGO_EXPORT_PROLOGUE_H#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
#endif#endif/* Start of preamble from import "C" comments.  *//* End of preamble from import "C" comments.  *//* Start of boilerplate cgo prologue.  */
#line 1 "cgo-gcc-export-header-prolog"#ifndef GO_CGO_PROLOGUE_H
#define GO_CGO_PROLOGUE_Htypedef signed char GoInt8;
typedef unsigned char GoUint8;
typedef short GoInt16;
typedef unsigned short GoUint16;
typedef int GoInt32;
typedef unsigned int GoUint32;
typedef long long GoInt64;
typedef unsigned long long GoUint64;
typedef GoInt64 GoInt;
typedef GoUint64 GoUint;
typedef __SIZE_TYPE__ GoUintptr;
typedef float GoFloat32;
typedef double GoFloat64;
typedef float _Complex GoComplex64;
typedef double _Complex GoComplex128;/*static assertion to make sure the file is being used on architectureat least with matching size of GoInt.
*/
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef _GoString_ GoString;
#endif
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;#endif/* End of boilerplate cgo prologue.  */#ifdef __cplusplus
extern "C" {
#endifextern GoString Test(GoSlice strs);
extern void Close();
extern GoMap Test2(GoMap input);#ifdef __cplusplus
}
#endif

可以看到,Go函数的切片在头文件中是GoSlice类型,包含指向实际数据的指针、数据的长度和slice的容量。切片中元素的类型是GoString,包含一个指向字符串地址的指针和字符串的长度。

注意:GoString是返回类型的时候,const char* p指向的字符串不是以尾0结束的,需要根据长度n自己拷贝使用!

C语言调用的示例代码如下

#include "stdio.h"
#include "memory.h"
#include "stdlib.h"
#include "malloc.h"
#include "string.h"
#include "libfunc.h"
int main()
{GoString sa[2] = {{"hello",strlen("hello")},{"world",strlen("world")}};GoSlice strs;strs.data = sa;strs.len =  2;strs.cap = 2;GoString ret = Test(strs);printf("ret(%d) is: %.*s\n",ret.n,ret.n,ret.p);//printf("ret: %s\n",ret.p);sleep(10);Close();printf("exit.\n");return 0;
}

可以看到打印Test返回结果的时候,要用"%.*s"打印,用"%s"的话会打出来一些古怪的东西。

有两点说明的地方:

1. C语言main退出后,会直接结束掉Go里的所有线程,defer得不到执行(这其实在go里也是一样),一般可以设计一个Close函数做清理工作。

2. 对于Go的map类型,头文件中是void*,是没办法在C中构造一个Go的map传给动态库的,也没法从动态库获取一个Go的map。有一些workaround,比如把map转为json字符串,或者传递两个数组分别为key和value。

C语言调用Go生成的动态库中的函数相关推荐

  1. 记录一次C语言调用go生成的动态库的踩坑过程

    记录一次C语言调用go生成的动态库的踩坑过程 问题现象 由于某些特殊原因,需要在C语言中调用go语言生成的so,本来挺顺利,一切都运行的很好.突然某一天,不知道怎么回事,再一个新程序中无法正常运行了, ...

  2. 【C 语言】动态库封装与设计 ( 动态库调用环境搭建 | 创建应用 | 拷贝动态库相关文件到源码路径 | 导入头文件 | 配置动态库引用 | 调用动态库中的函数 )

    文章目录 一.在 Visual Studio 2019 中创建 " 控制台应用 " 程序 二.拷贝 xxx.lib.xxx.dll.xxx.h 到源码路径 三.导入 xxx.h 头 ...

  3. qt中调用matlab生成的动态库

    前言: 前面已经实现了在vc中调用matlab生成的动态库,请参考:vc中调用matlab生成的动态库 现在在前面已经生成好的matlab动态库的基础上,在qt中调用matlab生成的动态库.生成ma ...

  4. windows7下,Java中利用JNI调用c++生成的动态库的使用步骤

    1.从http://www.oracle.com/technetwork/java/javase/downloads/jdk-7u2-download-1377129.html下载jdk-7u2-wi ...

  5. 【Android 逆向】Android 进程注入工具开发 ( 注入代码分析 | 获取注入的 libbridge.so 动态库中的 load 函数地址 并 通过 远程调用 执行该函数 )

    文章目录 一.dlsym 函数简介 二.获取 目标进程 linker 中的 dlsym 函数地址 三.远程调用 目标进程 linker 中的 dlsym 函数 获取 注入的 libbridge.so ...

  6. 「Python」python调用单个C++文件生成的动态库(.so)Part I

    环境说明 系统:Ubuntu 18.04 python:python 2.7.17 额外环境 上面的环境是普通测试,但是最终标题中的任务我需要在docker中执行,很多块内容我也不太懂,所以一步一步测 ...

  7. 【Qt】Qt6调用Visual Studio2019生成的动态库详解

    00. 目录 文章目录 00. 目录 01. 开发环境 02. Visual Studio 2019生成动态库 03. 新建Qt项目 04. 编写测试程序 05. 其它参考 06. 附录 01. 开发 ...

  8. 【转】matlab与C/C++混合编程——在Windows/Linux上调用Matlab编译的动态库文件

    转自:matlab与C/C++混合编程--在Windows/Linux上调用Matlab编译的动态库文件_sinat_18131557的博客-CSDN博客 date version comments ...

  9. Golang生成C动态库.so和静态库.a

    Go 生成C动态库.so和静态库.a 源代码 package mainimport "C" import "fmt"//export hello func he ...

最新文章

  1. 刨根问底,Kafka消息中间件到底会不会丢消息
  2. Spark Streaming揭秘 Day9 从Receiver的设计到Spark框架的扩展
  3. [Dynamic Language] Python3.7 源码安装 ModuleNotFoundError: No module named '_ctypes' 解决记录...
  4. Android 布局中 如何使控件居中
  5. 区块链技术 好文收藏
  6. java servlet是单例吗_关于java:为什么apache servlet是单例?
  7. 【软件测试】测试驱动开发
  8. 一步一步教你使用AgileEAS.NET基础类库进行应用开发-WinForm应用篇-演示使用报表构建UI-入库业务查询模块...
  9. Intel 64/x86_64/IA-32/x86处理器 - 指令格式(8) - 80386/32位指令前缀
  10. 采用Bert进行中文分词
  11. 机器学习基础算法27-聚类实战
  12. windows下安装python包管理器pip
  13. sonar mysql svn_jenkins+sonarqube+svn/git踩过的坑
  14. Spring Aop源码解读
  15. Java学习代码合集
  16. uniApp实现h5页面唤醒app
  17. Java 错别字检查接口 API
  18. 通过互联网进行远程桌面连接
  19. VMware 虚拟机三种网络模式详解
  20. CTex+WinEdt下载

热门文章

  1. FastDFS集群环境搭建
  2. android 自定义拍照录像
  3. [Vue3实操] 开发Todo List
  4. 想做一个 基于安卓的智能手机校园订餐系统 求各位大神指导 提供宝贵意见 感谢你们!
  5. 目标检测算法——工业缺陷数据集汇总2(附下载链接)
  6. nodejs+express搭建服务器
  7. linux磁盘及文件系统之四swap文件系统
  8. Good tool for creating excel : NPOI
  9. 高通安卓msm8909适配gt1xx系列的TP
  10. 深度学习目标检测2013-2018单双阶段主流模型概览及详解