工作中遇到的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. thinkpad重装系统不引导_重装系统时,如何判断Windows的启动方式是Legacy还是UEFI?...
  2. python PIL 单张图像变换大小—— img.resize()
  3. editActionsForRowAtIndexPath(iOS8) tableview编辑(删除、插入、移动)
  4. 你必须承认电子计算机是天之骄子,天之骄子造句
  5. [导入]竟然支持OpenGL ES!
  6. 【笛卡尔树】【线段树】meetings 会议(P5044)
  7. 反序列化 jackson_使用Jackson和Super类型令牌的Json反序列化
  8. 非常全面的阿里的Java面试题目,涵盖Java基础+高级+架构
  9. opencv24-直方图比较
  10. [转载]QMessageBox 用法
  11. 拓端tecdat|R语言使用bootstrap和增量法计算广义线性模型(GLM)预测置信区间
  12. java .class 反编译工具推荐
  13. 基于Java毕业设计大学生旅游拼团网站源码+系统+mysql+lw文档+部署软件
  14. 计算机配件有没先后顺序,内存插槽是否有优先顺序?
  15. 源码:Mybatis的LogFactory生成逻辑
  16. HtmlHelp调用chm帮助文档使用
  17. 双路cpu比单路强多少_别傻了!双核和双路服务器根本不一样
  18. 尚硅谷github案例
  19. 实验(1)信号的采样
  20. SublimeLinter进行PHP代码检查

热门文章

  1. 2021年中国电视机行业供需及主要企业经营情况分析[图]
  2. 第27次CCF计算机软件能力认证
  3. 医疗健康大数据: 应用实例与系统分析(转)
  4. 2021,打工人新的一年!
  5. Siemens:解决 “STEP 7 Professional 的许可无法彻底完成” 问题
  6. 全新数字荣誉墙-人脸识别扫码分享朋友圈大屏触控互动
  7. java printwriter用法_Java中printwriter类的用法 | 学步园
  8. 技术面试问项目难题如何解决的_技术面试感觉什么都会,面试官一问回答不上来怎么办?...
  9. 全基因组重测序基础分析
  10. 精确移相电路的设计举例