命令记不住

登陆

CGO 混合编程

CGO混合编程

1.CGO环境准备

Go 有强烈的 C 背景,除了语法具有继承性外,其设计者以及其设计目标都与C语言有着千丝万缕的联系

在Go与C语言互操作方面,Go更是提供了强大的支持。尤其是在Go中使用C,甚至可以直接在Go源文件中编写C代码.

在如下一些场景中,可能会涉及到Go与C的互操作:

1.提升局部代码性能时,用C替换一些Go代码。C之于Go,好比汇编之于C

2.嫌Go内存GC性能不足,自己手动管理应用内存

3.实现一些库的Go Wrapper。比如Oracle提供的C版本OCI,但Oracle并未提供Go版本的以及连接DB的协议细节,因此只能通过包装C版本OCI版本的方式以提供Go开发者使用

  • 前置条件

使用CGO特性,需要安装C/C构建工具链;

在macOS和Linux下安装GCC,使用命令安装:yum install gcc;

在windows下是需要安装MinGW工具 http://www.mingw-w64.org/doku.php;

同时需要保证环境变量CGO_ENABLED被设置为1,这表示CGO是被启用的状态。在本地构建时CGO_ENABLED默认是启用的,当交叉构建时CGO默认是禁止的;

通过 import "C" 语句启用CGO特性;

2.编译流程

源代码  -->预处理器  -->编译器  -->目标代码  -->链接器  -->可执行程序
  • 通过gcc查看编译过程
//分步编译
1.预处理: 宏定义展开 头文件展开 条件编译 去掉注释
gcc -E hello.c -o hello.i
2.编译: 检查语法 将源码编成汇编语言
gcc -S hello.i -o hello.s
3.汇编: 将汇编语言变成机器语言(二进制语言)
gcc -c hello.s -o hello.o
4.链接: 链接外部库
gcc hello.o -o hello
  • windows上编译Linux可执行二进制文件
SET CGO_ENABLED=0  SET GOOS=linux  SET GOARCH=amd64 go build main.go

//CGO_ENABLED是否使用cgo编译,0为不使用,1为使用,使用cgo进行交叉编译时需要编译机器安装对应的cgo程序.
//GOOS目标操作系统标识,windows对应Windows操作系统exe可执行文件,darwin对应Mac可执行文件,linux对应Linux可执行文件,freebsd对应UNIX系统.
//GOARCH目标可执行程序系统架构,包括386,amd64,arm.
//go build后接所编译程序的入口文件.
  • Linux上编译Windows可执行二进制文件
CGO_ENABLED=0  GOOS=windows  GOARCH=amd64 
go build main.go
  • 在Go语言中查看汇编语言代码
#将Go语言程序显示为汇编语言
go build -gcflags "-N -l"
go tool objdump -s 'main.Demo' -s ./Go程序.exe

3.import "C"

如果在Go代码中出现了 import "C"语句则表示使用了CGO特性,紧跟在这行语句前面的注释是一种特殊语法,里面包含的是正常的C语言代码。

当确保CGO启用的情况下,还可以在当前目录中包含C/C++对应的源文件。

package main

//以块注释的形式写C语言代码
/*
#include <stdio.h>
//C语言函数格式
void Print()
{
printf("x86cloud~");
}
*/
import "C"

func main() {
	C.Print()
}

C语言和Go语言混合编程需要安装GCC,同时编译速度相对会变慢

4.CGO语句

可以通过#cgo语句 设置编译阶段和链接阶段的相关参数

编译阶段的参数主要用于定义相关宏和指定头文件检索路径。

链接阶段的参数主要是指定库文件检索路径和要链接的库文件。

CFLAGS部分: -D 部分定义了宏 PORT,值为8080; -I 定义了头文件包含的检索目录。
LDFLAGS部分: -L 指定了链接时库文件检索目录; -l 指定了链接时需要链接libevent库。
  • #cgo
// #cgo CFLAGS: -D PORT = 8080 -I./include
// #cgo LDFLAGS: -L/usr/local/lib -llibevent
// #include <libevent.h>
import "C"

5.go和c类型转换

在Go语言中访问C语言的符号时,一般是通过虚拟的“C”包访问,比如C.int对应C语言的int类型。

Go语言中数值类型和C语言数据类型基本上是相似的,以下是对应关系表。

C语言类型 CGO类型 Go语言类型
char C.char byte
singed char C.schar int8
unsigned char C.uchar uint8
short C.short int16
unsigned short C.short 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

如果Go语言在调用C语言函数时需要传递参数或者接受返回值,需要进行类型转换

  • CGO类型转换示例代码
package main

//以块注释的形式写C语言代码

/*
#include <stdio.h>
//C语言函数格式
int add(int a, int b)
{
return a + b;
}
*/
import "C"
import "fmt"

func main() {
	a := 10
	b := 20
	c := C.add(C.int(a), C.int(b))
	fmt.Println(c)
	fmt.Printf("%T\n", c)
	//转成Go语言整型
	fmt.Println(int(c))
}
推荐阅读