乐趣区

关于go:快速上手-Go-CGO掌握在-Go-里写-C

大家好,我是煎鱼。

最近因为各种奇怪的起因,接触到了 Go 特色之一 CGO。这方面的相干内容也绝对少一些,给大家抛砖引玉。

毕竟很多跨语言调用,还是会依赖 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 看到:

一旦敞开就会影响 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 语言类型
char C.char byte
singed char C.schar int8
unsigned char C.uchar uint8
short C.short int16
unsigned short C.ushort uint16
int C.int int32
unsigned int C.uint uint32
long C.long int32
unsigned long C.ulong uint32
long long int C.longlong int64
unsigned long long int C.ulonglong uint64
float C.float float32
double C.double float64
size_t C.size_t uint

注意事项

应用 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 方面的内容。

文章继续更新,能够微信搜【脑子进煎鱼了】浏览,本文 GitHub github.com/eddycjy/blog 已收录,学习 Go 语言能够看 Go 学习地图和路线,欢送 Star 催更。

Go 图书系列

  • Go 语言入门系列:初探 Go 我的项目实战
  • Go 语言编程之旅:深刻用 Go 做我的项目
  • Go 语言设计哲学:理解 Go 的为什么和设计思考
  • Go 语言进阶之旅:进一步深刻 Go 源码

举荐浏览

  • Go 在信创这一块会输给 Java,想不通。。。
  • Go 错误处理:100+ 提案全副被回绝,为何现阶段仍用 if err != nil?
  • Go 为什么不像 Rust 用?!做错误处理?
退出移动版