# cgo
# cgo 是什么
cgo 是 Go 语言的一个特性,它允许 Go 代码调用 C 代码。cgo 可以让 Go 程序使用 C 语言编写的库,也可以让 Go 程序调用 C 语言编写的程序。
# cgo 语法
cgo 的语法比较简单,只需要在 Go 代码中包含一个特殊的注释即可。例如:
/* | |
#cgo CFLAGS: -I/path/to/c/include | |
#cgo LDFLAGS: -L/path/to/c/library -lmylib | |
#include <myheader.h> | |
*/ | |
import "C" | |
// Go 代码 |
上面的注释告诉 cgo 在编译 Go 代码时,需要使用 -I/path/to/c/include 和 -L/path/to/c/library -lmylib 这两个编译选项。同时,它还包含了 C 语言的头文件 <myheader.h> 。
# cgo 类型转换
cgo 提供了一些特殊的类型,用于在 Go 和 C 之间进行类型转换。这些类型包括:
| C 语言类型 | cgo 类型(写在 Go 里) | Go 对应类型 | 说明 |
|---|---|---|---|
char |
C.char |
byte 或 int8 |
C 的 char 是有符号的(大多数平台) |
signed char |
C.schar |
int8 |
明确指定有符号 |
unsigned char |
C.uchar |
uint8 |
通常用于 []byte |
short |
C.short |
int16 |
16 位有符号整数 |
unsigned short |
C.ushort |
uint16 |
16 位无符号整数 |
int |
C.int |
int32 |
在 cgo 中 C.int 固定为 32 位 |
unsigned int |
C.uint |
uint32 |
32 位无符号 |
long |
C.long |
平台相关 ( int32 或 int64 ) |
Linux x64 通常是 64 位;Windows 通常是 32 位 |
unsigned long |
C.ulong |
平台相关 ( uint32 或 uint64 ) |
同上 |
long long |
C.longlong |
int64 |
明确 64 位 |
unsigned long long |
C.ulonglong |
uint64 |
明确 64 位 |
float |
C.float |
float32 |
单精度浮点 |
double |
C.double |
float64 |
双精度浮点 |
_Bool / bool |
C.bool |
bool |
注意 C99 才支持 _Bool |
size_t |
C.size_t |
uint 或 uintptr |
通常无符号,用于内存长度 |
ptrdiff_t |
C.ptrdiff_t |
int 或 intptr |
指针差值 |
void* |
unsafe.Pointer 或 C.void* |
unsafe.Pointer |
通用指针 |
const char* |
*C.char |
string (需转换) |
常用于字符串 |
char* |
*C.char |
[]byte 或 string |
常用于输入输出缓冲 |
struct myStruct |
C.struct_myStruct |
自定义 Go 结构体(需手动映射) | cgo 不自动转结构体 |
enum MyEnum |
C.enum_MyEnum |
int |
枚举在 Go 里表现为整数 |
typedef int MYTYPE; |
C.MYTYPE |
对应基础类型 | typedef 直接映射 |
void |
无 | func() 或 nil |
用于无返回值函数 |
# #cgo 语句
在 import "C" 语句前额注释中可以通过 #cgo 语句设置编译阶段和链接阶段的相关参数。
编译阶段的参数主要用于定义相关宏和指定头文件检索路径。
链接阶段的参数主要是指定库文件检索路径和要链接的库文件。
#cgo 语句主要影响以下几个编译器环境变量:
- CFLAGS:编译器标志,用于指定 C 编译器的编译选项,例如
-I、-D等。- 例如:
#cgo CFLAGS: -I/path/to/c/include,#cgo CFLAGS: -DDEBUG等。
- 例如:
- LDFLAGS:链接器标志,用于链接阶段的选项,例如
-L、-l等。- 例如:
#cgo LDFLAGS: -L/path/to/c/library,#cgo LDFLAGS: -lmylib等。
- 例如:
- CXXFLAGS:C++ 编译器标志,用于 C++ 代码的编译选项,例如
-I、-D等。- 例如:
#cgo CXXFLAGS: -I/path/to/cxx/include,#cgo CXXFLAGS: -DDEBUG等。
- 例如:
- CPPFLAGS:C&C++ 预处理器标志,在 C 和 C++ 编译前的预处理阶段使用,例如
-I、-D等。- 例如:
#cgo CPPFLAGS: -I/path/to/cxx/include,#cgo CPPFLAGS: -DDEBUG等。
- 例如:
#cgo 指令还支持条件选择,当满足某个操作系统或某个 CPU 架构类型时后面的编译或链接选项失效。比如下面分别针对 windows 和 非 windows 下平台的编译和链接选项:
// #cgo windows CFLAGS: -DX86=1 // 在 windows 平台下,编译前会预定义 X86 宏为 1 | |
// #cgo !windows LDFLAGS: -lm // 在非 windows 平台下,在链接阶段会要求链接 math 数据库。 |
如果在不同的系统下 cgo 对应着不同的 c 代码,我们可以先使用 #cgo 指令定义不同的 C 语言的宏,然后通过宏来区分不同的代码:
package main | |
/* | |
#cgo windows CFLAGS: -DCGO_OS_WINDOWS=1 | |
#cgo darwin CFLAGS: -DCGO_OS_DARWIN=1 | |
#cgo linux CFLAGS: -DCGO_OS_LINUX=1 | |
#if defined(CGO_OS_WINDOWS) | |
const char* os = "windows"; | |
#elif defined(CGO_OS_DARWIN) | |
const char* os = "darwin"; | |
#elif defined(CGO_OS_LINUX) | |
const char* os = "linux"; | |
#else | |
# error(unknown os) | |
#endif | |
*/ | |
import "C" | |
func main() { | |
print(C.GoString(C.os)) | |
} |
// #cgo CFLAGS: -DPNG_DEBUG=1 -I./include | |
// #cgo LDFLAGS: -L/usr/local/lib -lpng | |
// #include <stdio.h> | |
import "C" |
# cgo 注意事项
- cgo 代码中不能使用 Go 的并发特性,例如 goroutine 和 channel。
- cgo 代码中不能使用 Go 的错误处理机制,例如 panic 和 recover。
- cgo 代码中不能使用 Go 的反射机制,例如 reflect 包。
- cgo 代码中不能使用 Go 的接口类型,例如 interface {}。
- cgo 代码中不能使用 Go 的切片类型,例如 [] T。
- cgo 代码中不能使用 Go 的 map 类型,例如 map [K] V。