目的

现代的各种高级的编程语言很多都是在C语言之上构建的,基本上也都能够调用C语言,并且这个在某些情况下也是有这个需求和存在的意义的。Go语言对这方面支持的挺不错,不光可以调用C语言,还能给C语言调用。这篇文章将对相关的内容做个说明。

基础说明

Go语言工具包中有一个Cgo命令,它用来处理Go调用C相关操作。 我们可以直接使用该命令,也可以在运行或构建Go程序时自动调用它。

go env -w CGO_ENABLED=1CCCXX
Go中调用C
Cimport "C"#include ...C
.c.s.S.sx.cc.cpp.cxx

需要注意的是C++中的重载和类方法等C不支持的语法想要在Go中使用都需要用C语言标准函数包装一层,使用方法就和C语言中调用C++一样。

C中调用Go
go clean
//export funcnamego build -buildmode=c-shared -o libxxx.so
go build -buildmode=c-archive -o libxxx.a
数据类型差异

两个语言间调用其实就是数据的传递处理,需要注意的是因为两者毕竟不是同一种语言所以两者之间可以交互的数据类型是有限制的。有些时候也会有强制转换数据类型的需求,比如下面这个代码:

package rand

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

func Random() int {
    return int(C.random()) // C函数返回值给Go,random的返回值是long类型
}

func Seed(i int) {
    C.srandom(C.uint(i)) // Go传值给C的函数,srandom函数接收uint类型数据
}

两者间可用的基本数值类型转换有下面一些:

C.char,       C.schar (signed char),       C.uchar (unsigned char)
C.short,       C.ushort (unsigned short)
C.int,       C.uint (unsigned int)
C.long,       C.ulong (unsigned long)
C.longlong (long long),       C.ulonglong (unsigned long long)
C.float,       C.double
C.complexfloat (complex float),       C.complexdouble (complex double)
void*unsafe.Pointer
__int128_t__uint128_t[16]byte

C中函数传输参数为数组的话直接传递数组名就行,在Go中向这类函数传递数组需要传递数组第一个元素的地址,另外需要注意的是数组中元素也必须是C语言中支持的类型:

C.f(&C.arr[0])

C中并没有string类型,使用字符串时需要进行处理:

package print

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

func Print(s string) {
    cs := C.CString(s) // 这个方式会将字符串拷贝一份,返回指针,注意使用完需要释放内存
    defer C.free(unsafe.Pointer(cs)) // defer修饰的语句会在该函数退出前执行
    C.fputs(cs, (*C.FILE)(C.stdout))
}
*C.charC.GoString()
structunionenumstruct_xxxunion_xxxenum_xxxx._name
sizeofC.sizeof_T
总结

总的来说Go语言与C语言相互调用并不复杂,更多内容可以参考下面链接:
https://go.dev/blog/cgo
https://pkg.go.dev/cmd/cgo

另外在Go中使用C语言除了Cgo以外还可以使用swig来实现:
https://swig.org/