Go语言高级编程

324课时
1.4K学过
8分

2. 第2章 CGO编程

引言

2.1 快速入门

2.1.1 最简CGO程序

2.1.2 基于C标准库函数输出字符串

2.1.3 使用自己的C函数

2.1.4 C代码的模块化

2.1.5 用Go重新实现C函数

2.1.6 面向C接口的Go编程

2.2 CGO基础

2.2.1 import "C"语句

2.2.2 #cgo语句

2.2.3 build tag 条件编译

2.3 类型转换

2.3.1 数值类型

2.3.2 Go 字符串和切片

2.3.3 结构体、联合、枚举类型

2.3.4 数组、字符串和切片

2.3.5 指针间的转换

2.3.6 数值和指针的转换

2.3.7 切片间的转换

2.4 函数调用

2.4.1 Go调用C函数

2.4.2 C函数的返回值

2.4.3 void函数的返回值

2.4.4 C调用Go导出函数

2.5 内部机制

2.5.1 CGO生成的中间文件

2.5.2 Go调用C函数

2.5.3 C调用Go函数

2.6 实战: 封装qsort

2.6.1 认识qsort函数

2.6.2 将qsort函数从Go包导出

2.6.3 改进:闭包函数作为比较函数

2.6.4 改进:消除用户对unsafe包的依赖

2.7 CGO内存模型

2.7.1 Go访问C内存

2.7.2 C临时访问传入的Go内存

2.7.3 C长期持有Go指针对象

2.7.4 导出C函数不能返回Go内存

2.8 C++ 类包装

2.8.1 C++ 类到 Go 语言对象

2.8.1.1 准备一个 C++ 类

2.8.1.2 用纯C函数接口封装 C++ 类

2.8.1.3 将纯C接口函数转为Go函数

2.8.1.4 包装为Go对象

2.8.2 Go 语言对象到 C++ 类

2.8.2.1 构造一个Go对象

2.8.2.2 导出C接口

2.8.2.3 封装C++对象

2.8.2.4 封装C++对象改进

2.8.3 彻底解放C++的this指针

2.9 静态库和动态库

2.9.1 使用C静态库

2.9.2 使用C动态库

2.9.3 导出C静态库

2.9.4 导出C动态库

2.9.5 导出非main包的函数

2.10 编译和链接参数

2.10.1 编译参数:CFLAGS/CPPFLAGS/CXXFLAGS

2.10.2 链接参数:LDFLAGS

2.10.3 pkg-config

2.10.4 go get 链

2.10.5 多个非main包中导出C函数

课程评价 (0)

请对课程作出评价:
0/300

学员评价

暂无精选评价
15分钟

2.3.1 数值类型

在Go语言中访问C语言的符号时,一般是通过虚拟的“C”包访问,比如C.int对应C语言的int类型。有些C语言的类型是由多个关键字组成,但通过虚拟的“C”包访问C语言类型时名称部分不能有空格字符,比如unsigned int不能直接通过C.unsigned int访问。因此CGO为C语言的基础数值类型都提供了相应转换规则,比如C.uint对应C语言的unsigned int

Go语言中数值类型和C语言数据类型基本上是相似的,以下是它们的对应关系表2-1所示。

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

表 2-1 Go语言和C语言类型对比

需要注意的是,虽然在C语言中intshort等类型没有明确定义内存大小,但是在CGO中它们的内存大小是确定的。在CGO中,C语言的intlong类型都是对应4个字节的内存大小,size_t类型可以当作Go语言uint无符号整数类型对待。

CGO中,虽然C语言的int固定为4字节的大小,但是Go语言自己的intuint却在32位和64位系统下分别对应4个字节和8个字节大小。如果需要在C语言中访问Go语言的int类型,可以通过GoInt类型访问,GoInt类型在CGO工具生成的_cgo_export.h头文件中定义。其实在_cgo_export.h头文件中,每个基本的Go数值类型都定义了对应的C语言类型,它们一般都是以单词Go为前缀。下面是64位环境下,_cgo_export.h头文件生成的Go数值类型的定义,其中GoIntGoUint类型分别对应GoInt64GoUint64

typedef signed char GoInt8;
typedef unsigned char GoUint8;
typedef short GoInt16;
typedef unsigned short GoUint16;
typedef int GoInt32;
typedef unsigned int GoUint32;
typedef long long GoInt64;
typedef unsigned long long GoUint64;
typedef GoInt64 GoInt;
typedef GoUint64 GoUint;
typedef float GoFloat32;
typedef double GoFloat64;

除了GoIntGoUint之外,我们并不推荐直接访问GoInt32GoInt64等类型。更好的做法是通过C语言的C99标准引入的<stdint.h>头文件。为了提高C语言的可移植性,在<stdint.h>文件中,不但每个数值类型都提供了明确内存大小,而且和Go语言的类型命名更加一致。Go语言类型<stdint.h>头文件类型对比如表2-2所示。

C语言类型

CGO类型

Go语言类型

int8_t

C.int8_t

int8

uint8_t

C.uint8_t

uint8

int16_t

C.int16_t

int16

uint16_t

C.uint16_t

uint16

int32_t

C.int32_t

int32

uint32_t

C.uint32_t

uint32

int64_t

C.int64_t

int64

uint64_t

C.uint64_t

uint64

表 2-2 <stdint.h>类型对比

前文说过,如果C语言的类型是由多个关键字组成,则无法通过虚拟的“C”包直接访问(比如C语言的unsigned short不能直接通过C.unsigned short访问)。但是,在<stdint.h>中通过使用C语言的typedef关键字将unsigned short重新定义为uint16_t这样一个单词的类型后,我们就可以通过C.uint16_t访问原来的unsigned short类型了。对于比较复杂的C语言类型,推荐使用typedef关键字提供一个规则的类型命名,这样更利于在CGO中访问。