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

大家好,最近因为各种奇怪的原因,接触到了 Go 特色之一 CGO。这方面的相关内容也相对少一些,给大家抛砖引玉,有经验的大佬欢迎补充。

f753b4bec073f8144b4050419c5eb7ea.png
图片来源于 marlin

毕竟很多跨语言调用,还是会依赖 CGO 这个特性。希望大家在真正要用时有个前置知识垫肚子。

CGO 是什么

CGO 就是 C 和 Go,两个编程语言。指的是能够创建调用 C 代码的 Go 包。对照着 Go 代码中的 “C”:

package main

import "C"

func main() {}

一旦程序中出现 import "C",则意味着开启 CGO 特性。在进行 go build 等阶段时,将会调用 C 编译器(通常是 gcc 或 clang)。

CGO 对应的环境变量是 CGO_ENABLED,设置为 1 则开启 CGO,为 0 则关闭 CGO。

编译命令如下:

CGO_ENABLED=0 go build -o hellojy main.go

当然,对于默认值。该环境变量值为 1,C 编译器也是使用 gcc。我们可以通过 go env 看到:

003f6194f1d29bf86a9572e3e9457a59.png

一旦关闭就会影响 CGO 编译。需要特别留意,交叉编译时会默认关闭 CGO。

CGO 快速上手

最小 Demo

先来一个 CGO 的 Go 例子:

package main

//#include <stdio.h>
import "C"

func main() {
 s := C.CString("hello world.")
 C.puts(s)
}

运行 go run main.go,输出结果:

hello world.

声明 C 注解

如果你没有了解过 CGO,看到上面的例子,可能会有好几个疑问。

首先是 include:

//#include <stdio.h>
import "C"

import "C" 我们懂,是导入 C 的伪包。前面的注解是什么?

无论是:

//#include <stdio.h>

又或是:

/*
#include <stdio.h>
#include <stdlib.h>
*/

实际上这是导入 C 前的注解,注解内容可以包含任何 C 代码,例如:函数、变量的声明定义、库引用等。(该注解要紧挨导入语句)

回到 Demo 本身,如果我们去掉 //#include <stdio.h>,再运行会出现如下报错:

# command-line-arguments
./main.go:7:2: could not determine kind of name for C.puts

去掉后,语句 C.puts(s) 将无法运行。

实际上 stdio.h 的全称是:standard input output.header(标准输入输出头文件)。该文件大都是些输入输出函数的声明,引用了这库,就能使用 C 的 puts 方法。

其他同理,你在注解中声明、定义的东西,均可以在 Go 代码中通过 C 这个伪包来引用和调用。

其次像是 CString 方法,属于在 Go 和 C 类型之间需要复制数据的特殊函数,伪包 C 有进行预定义。

例如:

func C.CString(string) *C.char
func C.CBytes([]byte) unsafe.Pointer
func C.GoString(*C.char) string
func C.GoStringN(*C.char, C.int) string
func C.GoBytes(unsafe.Pointer, C.int) []byte

Go 和 C 类型对照

Go 官方有提供一份基础类型的对照表,大家可以参照来使用和理解。

如下:

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

注意事项

使用 CGO,除了会带来一定的性能损耗外。需要特别注意的是:内存泄露。因为 Go 是带垃圾回收机制的编程语言,而使用了 C 后,需要手动的管理内存。

还是这个 Demo:

package main

//#include <stdio.h>
import "C"

func main() {
 s := C.CString("hello world.")
 C.puts(s)
}

如果这是一个常驻进程,也没有任何释放动作。用 C.CString 方法所申请的变量 s 就会泄露。

因此与 “C” 相关的变量创建,需要进行手动的内存管理。正确的代码如下:

/*
#include <stdio.h>
#include <stdlib.h>
*/
import "C"
import (
 "unsafe"
)

func main() {
 b := C.CString("hello world.")
 C.puts(b)
 C.free(unsafe.Pointer(b))
}

需要调用 C.free 方法进行主动的内存释放。如果该程序自然结束,也会自动回收。

总结

在今天这篇文章中,我们介绍了 Go 语言中 CGO 的基础知识和快速入门。整体上,只要适应了写法,CGO 的用法就不算太麻烦。

需要特别注意手动内存管理、性能损耗等多方面的制约。后续我们也会继续深入 CGO 方面的内容。

- END -

扫码关注公众号「网管叨bi叨」

给网管个星标,第一时间吸我的知识 👆

网管整理了一本《Go 开发参考书》收集了70多条开发实践。去公众号回复【gocookbook】领取!还有一本《k8s 入门实践》讲解了常用软件在K8s上的部署过程,公众号回复【k8s】即可领取!

觉得有用就点个在看  👇👇👇

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
"CGo是Go语言的一个特性,用于在Go语言程序中调用C语言的函数和访问C语言的数据结构。但是,当我们使用命令'go build'或'go install'编译或安装一个使用CGo的Go语言项目时,有时会遇到错误消息'cgo go: no go source files'。 这个错误消息的意思是在当前目录下没有找到任何的Go源文件。造成这个错误的原因可能是以下几种情况: 1. 当前目录下确实没有任何的Go源文件。这时我们需要检查项目目录结构,确保所有Go源文件的位置和命名都正确。 2. 当前目录下只有C文件,并且没有Go源文件。CGo需要至少一个Go源文件来实现C语言的函数和数据结构的绑定。我们需要为项目添加一个或多个Go源文件来解决这个问题。 3. 当前目录下有Go源文件,但它们没有被正确命名或没有包含有效的Go代码。我们需要检查Go源文件的命名和内容,确保它们符合Go语言的语法和规范。 为了解决'cgo go: no go source files'错误,我们可以采取以下步骤: 1. 确认当前目录下是否有正确命名和包含有效Go代码的Go源文件。 2. 如果当前目录下只有C文件,需要添加至少一个Go源文件。 3. 检查项目目录结构,确保所有Go源文件的位置和命名都正确。 4. 确保使用正确的命令进行编译或安装,如'go build'或'go install'。 总之,当我们使用CGo时,如果遇到'cgo go: no go source files'错误,需要检查当前目录下是否有正确的Go源文件,并确认它们的命名和内容是否正确。同时,也要注意使用正确的命令进行编译或安装。"

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值