# 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 byteint8 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 平台相关 ( int32int64 ) Linux x64 通常是 64 位;Windows 通常是 32 位
unsigned long C.ulong 平台相关 ( uint32uint64 ) 同上
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 uintuintptr 通常无符号,用于内存长度
ptrdiff_t C.ptrdiff_t intintptr 指针差值
void* unsafe.PointerC.void* unsafe.Pointer 通用指针
const char* *C.char string (需转换) 常用于字符串
char* *C.char []bytestring 常用于输入输出缓冲
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))
}
o
// #cgo CFLAGS: -DPNG_DEBUG=1 -I./include
// #cgo LDFLAGS: -L/usr/local/lib -lpng
// #include <stdio.h>
import "C"

# cgo 注意事项

  1. cgo 代码中不能使用 Go 的并发特性,例如 goroutine 和 channel。
  2. cgo 代码中不能使用 Go 的错误处理机制,例如 panic 和 recover。
  3. cgo 代码中不能使用 Go 的反射机制,例如 reflect 包。
  4. cgo 代码中不能使用 Go 的接口类型,例如 interface {}。
  5. cgo 代码中不能使用 Go 的切片类型,例如 [] T。
  6. cgo 代码中不能使用 Go 的 map 类型,例如 map [K] V。
更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

ZJM 微信支付

微信支付

ZJM 支付宝

支付宝