golang cgo 使用总结

article/2025/8/30 1:23:28

文章目录

  • 参数传递
    • 基本数值类型
    • 切片
    • 字符串
    • 其他类型
  • 总结
  • 参考资料

CGO 提供了 golang 和 C 语言相互调用的机制。某些第三方库可能只有 C/C++ 的实现,完全用纯 golang 的实现可能工程浩大,这时候 CGO 就派上用场了。可以通 CGO 在 golang 在调用 C 的接口,C++ 的接口可以用 C 包装一下提供给 golang 调用。被调用的 C 代码可以直接以源代码形式提供或者打包静态库或动态库在编译时链接。推荐使用静态库的方式,这样方便代码隔离,编译的二进制也没有动态库依赖方便发布也符合 golang 的哲学。
CGO 的具体使用教程本文就不涉及了,这里主要介绍下一些细节避免使用 CGO 的时候踩坑。

参数传递

基本数值类型

golang 的基本数值类型内存模型和 C 语言一样,就是连续的几个字节(1 / 2 / 4 / 8 字节)。因此传递数值类型时可以直接将 golang 的基本数值类型转换成对应的 CGO 类型然后传递给 C 函数调用,反之亦然:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

 

package main

/*

#include <stdint.h>

static int32_t add(int32_t a, int32_t b) {

return a + b;

}

*/

import "C"

import "fmt"

func main() {

var a, b int32 = 1, 2

var c int32 = int32(C.add(C.int32_t(a), C.int32_t(b)))

fmt.Println(c) // 3

}

golang 和 C 的基本数值类型转换对照表如下:

C语言类型CGO类型Go语言类型
charC.charbyte
singed charC.scharint8
unsigned charC.ucharuint8
shortC.shortint16
unsigned shortC.ushortuint16
intC.intint32
unsigned intC.uintuint32
longC.longint32
unsigned longC.ulonguint32
long long intC.longlongint64
unsigned long long intC.ulonglonguint64
floatC.floatfloat32
doubleC.doublefloat64
size_tC.size_tuint

注意 C 中的整形比如 int 在标准中是没有定义具体字长的,但一般默认认为是 4 字节,对应 CGO 类型中 C.int 则明确定义了字长是 4 ,但 golang 中的 int 字长则是 8 ,因此对应的 golang 类型不是 int 而是 int32 。为了避免误用,C 代码最好使用 C99 标准的数值类型,对应的转换关系如下:

C语言类型CGO类型Go语言类型
int8_tC.int8_tint8
uint8_tC.uint8_tuint8
int16_tC.int16_tint16
uint16_tC.uint16_tuint16
int32_tC.int32_tint32
uint32_tC.uint32_tuint32
int64_tC.int64_tint64
uint64_tC.uint64_tuint64

切片

golang 中切片用起来有点像 C 中的数组,但实际的内存模型还是有点区别的。C 中的数组就是一段连续的内存,数组的值实际上就是这段内存的首地址。golang 切片的内存模型如下所示(参考源码 $GOROOT/src/runtime/chan.go):

由于底层内存模型的差异,不能直接将 golang 切片的指针传给 C 函数调用,而是需要将存储切片数据的内部缓冲区的首地址及切片长度取出传传递:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

 

package main

/*

#include <stdint.h>

static void fill_255(char* buf, int32_t len) {

int32_t i;

for (i = 0; i < len; i++) {

buf[i] = 255;

}

}

*/

import "C"

import (

"fmt"

"unsafe"

)

func main() {

b := make([]byte, 5)

fmt.Println(b) // [0 0 0 0 0]

C.fill_255((*C.char)(unsafe.Pointer(&b[0])), C.int32_t(len(b)))

fmt.Println(b) // [255 255 255 255 255]

}

字符串

golang 的字符串和 C 中的字符串在底层的内存模型也是不一样的:

golang 字串符串并没有用 ‘\0’ 终止符标识字符串的结束,因此直接将 golang 字符串底层数据指针传递给 C 函数是不行的。一种方案类似切片的传递一样将字符串数据指针和长度传递给 C 函数后,C 函数实现中自行申请一段内存拷贝字符串数据然后加上未层终止符后再使用。更好的方案是使用标准库提供的 C.CString() 将 golang 的字符串转换成 C 字符串然后传递给 C 函数调用:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

 

package main

/*

#include <stdint.h>

#include <stdlib.h>

#include <string.h>

static char* cat(char* str1, char* str2) {

static char buf[256];

strcpy(buf, str1);

strcat(buf, str2);

return buf;

}

*/

import "C"

import (

"fmt"

"unsafe"

)

func main() {

str1, str2 := "hello", " world"

// golang string -> c string

cstr1, cstr2 := C.CString(str1), C.CString(str2)

defer C.free(unsafe.Pointer(cstr1)) // must call

defer C.free(unsafe.Pointer(cstr2))

cstr3 := C.cat(cstr1, cstr2)

// c string -> golang string

str3 := C.GoString(cstr3)

fmt.Println(str3) // "hello world"

}

需要注意的是 C.CString() 返回的 C 字符串是在堆上新创建的并且不受 GC 的管理,使用完后需要自行调用 C.free() 释放,否则会造成内存泄露,而且这种内存泄露用前文中介绍的 pprof 也定位不出来。

其他类型

golang 中其他类型(比如 map) 在 C/C++ 中并没有对等的类型或者内存模型也不一样。传递的时候需要了解 golang 类型的底层内存模型,然后进行比较精细的内存拷贝操作。传递 map 的一种方案是可以把 map 的所有键值对放到切片里,然后把切片传递给 C++ 函数,C++ 函数再还原成 C++ 标准库的 map 。由于使用场景比较少,这里就不赘述了。

总结

本文主要介绍了在 golang 中使用 CGO 调用 C/C++ 接口涉及的一些细节问题。C/C++ 比较底层的语言,需要自己管理内存。使用 CGO 时需要对 golang 底层的内存模型有所了解。另外 goroutine 通过 CGO 进入到 C 接口的执行阶段后,已经脱离了 golang 运行时的调度并且会独占线程,此时实际上变成了多线程同步的编程模型。如果 C 接口里有阻塞操作,这时候可能会导致所有线程都处于阻塞状态,其他 goroutine 没有机会得到调度,最终导致整个系统的性能大大较低。总的来说,只有在第三方库没有 golang 的实现并且实现起来成本比较高的情况下才需要考虑使用 CGO ,否则慎用。

参考资料

  • https://golang.org/cmd/cgo/

http://chatgpt.dhexx.cn/article/wVTbxBqt.shtml

相关文章

CGO总结

概述 cgo作为一个强大的工具&#xff0c;可以让go轻松实现调用c语言的巨额遗产。。。 问题 实际开发中&#xff0c;发现使用go语言调用c的动态库时&#xff0c;经常出现传入c的字符串发生变化的现象&#xff0c;如下&#xff1a; 情况1 由a.txt变为a.txt$\n 情况2 由plai…

cgo 机制 - 从 c 调用 go

文&#xff5c;朱德江&#xff08;GitHub ID&#xff1a;doujiang24) MOSN 项目核心开发者 蚂蚁集团技术专家 专注于云原生网关研发的相关工作 本文 4656 字 阅读 12 分钟 一、前言 去年刚学 go 语言的时候&#xff0c;写了这篇 cgo 实现机制[1] &#xff0c;介绍了 cgo 的基本…

深入学习CGO

深入学习CGO 快速入门基础知识import "C" 语句#cgo语句 GO与C的类型转换CGO函数调用CGO内部机制CGO内存模型C类封装成C APICGO调用在go runtime 层面的处理CGO的静态/动态库封装以及编译链接参数CGO定位内存泄露CGO性能CGO最佳使用场景总结参考文献&#xff1a; 很多…

快速上手 CGO,掌握在 Go 里写 C!

大家好&#xff0c;最近因为各种奇怪的原因&#xff0c;接触到了 Go 特色之一 CGO。这方面的相关内容也相对少一些&#xff0c;给大家抛砖引玉&#xff0c;有经验的大佬欢迎补充。 图片来源于 marlin 毕竟很多跨语言调用&#xff0c;还是会依赖 CGO 这个特性。希望大家在真正要…

DevOps学习心得总结

流程步骤&#xff1a; 1、PLAN 制定计划 &#xff08;牢记交付给用户的目标&#xff09; 2、CODE 开始编码 &#xff08;使用相同的代码&#xff0c;不同版本的代码存储到仓库中&#xff0c;借助Git等工具在需要时合并【版本控制】&#xff09; 3、BUILD 构建阶段…

DevOps工具链

DevOps是敏捷研发中持续构建&#xff08;Continuous Build&#xff0c;CB&#xff09;、持续集成&#xff08;Continuous Integration&#xff0c;CI&#xff09;、持续交付&#xff08;Continuous Delivery&#xff0c;CD&#xff09;的自然延伸&#xff0c;从研发周期向右扩展…

DevOps及DevOps常用的工具介绍

目录 1. 什么是 DevOps2. DevOps 概念的起源2.1. 单体架构 瀑布模式2.2. 分布式架构 敏捷开发模式2.2.1. 多人协同开发问题2.2.2. 多机器问题2.2.3. 开发和运维角色的天生对立问题 2.3. 微服务架构 DevOps 3. DevOps 到底是什么4. DevOps 常用的工具4.1. Jenkins4.2. Kubern…

DevOps 简史

【注】本文节译自&#xff1a;https://www.bmc.com/blogs/devops-history/   IT 行业的当前状态受技术进步在整个历史中所产生的连锁效应所影响。不时出现的新技术极大地改变了世界运转的方式。最近&#xff0c;技术进步似乎开始以惊人的速度出现。自从互联网出现以来&#…

DevOps 学习

目录 一、概述 1、CI/CD简介 二、Git简介 三、Jenkins简介 一、概述 DevOps是Development和Operations的组合&#xff0c;也就是开发和运维的简写。 DevOps集文化理念、实践与工具于一身&#xff0c;可以提高组织高速交付应用程序和服务的能力&#xff0c;与使用传统软件…

DevOps实践

数字化时代&#xff0c;技术的交付速度和质量&#xff0c;直接关系业务的发展和创新。IT 技术交付和运行的效率&#xff0c;成为决定数字化转型成败的关键&#xff0c;而 DevOps 要解决的问题正在于此&#xff0c;DevOps 成为数字化转型的重要一环。 能力构建 随着云原生技术的…

DevOps的前世今生

导语 DevOps诞生已经13年了&#xff0c;你理解他吗&#xff1f; 为什么相伴了13年&#xff0c;你仍然对他不甚了了呢&#xff1f; 你真的以为DevOps是一个筐&#xff0c;什么东西都可以往里装吗&#xff1f; 你以为DevOps落地就是找一个JIRA&#xff08;敏捷管理工具&#…

Learning DevOps

什么是 DevOps DevOps&#xff08;Development & Operations&#xff09;/de’vps/ 是一组过程、方法与系统的统称&#xff0c;用于促进开发 (Dev)、技术运营 (Ops)和质量保障&#xff08;QA&#xff09;部门之间的沟通、协作与整合。 DevOps 的开发流程 软件从零开始到…

DevOps思想

什么是DevOps&#xff1f; DevOps是一种思想或方法论&#xff0c;它涵盖了开发、测试、运维的整个过程&#xff01;DevOps强调开发、测试、运维、质检(QA)部门之间的有效沟通与协作。强调通过自动化的方法管理软件变更、软件集成。使软件从构建到测试、发布更加快捷、可靠&…

DevOps的发展史

公众号关注 「奇妙的 Linux 世界」 设为「星标」&#xff0c;每天带你玩转 Linux &#xff01; — 1 — 可操作的概述 多亏了云计算和开源&#xff0c;软件开发的速度从几年缩短到几个月。每家公司都在向一个软件公司转变。DevOps 已迅速成为公司大规模开发和部署软件的最有效方…

DevOps——简析

节选自百度等资料 知乎解析连接 一、DevOps的目的 只有一个&#xff1a;提高开发到运维发布版本的效率。 1.初级应用&#xff1a;开发运维一体化 2.最高阶的应用&#xff1a;端到端的概念。 DevOps 的三大支柱之中&#xff0c;即人&#xff08;People&#xff09;、流程&…

DevOps推广实践总结

中大型团队在敏捷DevOps转型过程中常见的实践总结 目录 1、聘用外部DevOps顾问 2、建立DevOps共识 3、采用“DevOps改进”而非“DevOps转型” 4、构建“比学赶超”的组织氛围 5、规范化DevOps实践 1、聘用外部DevOps顾问 小型团队可以不用聘用昂贵的外部教练&#xff0c;因…

DevOps

DevOps 一、DevOps的由来和概念1. 由来2. DevOps概念解析&#xff08;1&#xff09;来自不同渠道和来源的定义&#xff1a;&#xff08;2&#xff09;其他摘录 二、DevOps 工作流程1. DevOps的好处与价值2. DevOps能力环 三、devops流程工具四、DevOps发展现状哪些互联网公司采…

Devops的概念

1、什么是DevOps&#xff1f; 答&#xff1a;DevOps是产品开发过程中开发&#xff08;Dev&#xff09;和运营&#xff08;Ops&#xff09;团队之间的灰色区域。DevOps是一种在产品开发周期中强调沟通&#xff0c;集成和协作的文化。因此&#xff0c;它消除了软件开发团队和运营…

DevOps—基本概念

DevOps—基本概念 1. DevOps2. CI/CD 1. DevOps 维基百科定义&#xff1a; DevOps是一组过程、方法与系统的统称&#xff0c;用于促进 开发、技术运营 和 质量保障&#xff08;QA&#xff09; 部门之间的沟通、协作与整合。我理解DevOps是一种软件管理思维模式。 为什么会有D…

DevOps简介

一、DevOps定义&#xff1a;Development和Operations的组合&#xff0c;突出重视软件开发人员与运维人员的沟通合作&#xff0c;通过自动化流程使得软件构建、测试、发布更加快捷、频繁和可靠。 它是一个完整的面向IT运维的工作流&#xff0c;以 IT 自动化以及持续集成&#xf…