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

学员评价

暂无精选评价
20分钟

3.5.2 if/goto跳转

Go语言刚刚开源的时候并没有goto语句,后来Go语言虽然增加了goto语句,但是并不推荐在编程中使用。有一个和cgo类似的原则:如果可以不使用goto语句,那么就不要使用goto语句。Go语言中的goto语句是有严格限制的:它无法跨越代码块,并且在被跨越的代码中不能含有变量定义的语句。虽然Go语言不推荐goto语句,但是goto确实每个汇编语言码农的最爱。因为goto近似等价于汇编语言中的无条件跳转指令JMP,配合if条件goto就组成了有条件跳转指令,而有条件跳转指令正是构建整个汇编代码控制流的基石。

为了便于理解,我们用Go语言构造一个模拟三元表达式的If函数:

func If(ok bool, a, b int) int {
	if ok { return a } else { return b }
}

比如求两个数最大值的三元表达式(a>b)?a:b用If函数可以这样表达:If(a>b, a, b)。因为语言的限制,用来模拟三元表达式的If函数不支持泛型(可以将a、b和返回类型改为空接口,不过使用会繁琐一些)。

这个函数虽然看似只有简单的一行,但是包含了if分支语句。在改用汇编实现前,我们还是先用汇编的思维来重新审视If函数。在改写时同样要遵循每个表达式只能有一个运算符的限制,同时if语句的条件部分必须只有一个比较符号组成,if语句的body部分只能是一个goto语句。

用汇编思维改写后的If函数实现如下:

func If(ok int, a, b int) int {
	if ok == 0 { goto L }
	return a
L:
	return b
}

因为汇编语言中没有bool类型,我们改用int类型代替bool类型(真实的汇编是用byte表示bool类型,可以通过MOVBQZX指令加载byte类型的值,这里做了简化处理)。当ok参数非0时返回变量a,否则返回变量b。我们将ok的逻辑反转下:当ok参数为0时,表示返回b,否则返回变量a。在if语句中,当ok参数为0时goto到L标号指定的语句,也就是返回变量b。如果if条件不满足,也就是ok参数非0,执行后面的语句返回变量a。

上述函数的实现已经非常接近汇编语言,下面是改为汇编实现的代码:

TEXT ·If(SB), NOSPLIT, $0-32
	MOVQ ok+8*0(FP), CX // ok
	MOVQ a+8*1(FP), AX  // a
	MOVQ b+8*2(FP), BX  // b

	CMPQ CX, $0         // test ok
	JZ   L              // if ok == 0, goto L
	MOVQ AX, ret+24(FP) // return a
	RET

L:
	MOVQ BX, ret+24(FP) // return b
	RET

首先是将三个参数加载到寄存器中,ok参数对应CX寄存器,a、b分别对应AX、BX寄存器。然后使用CMPQ比较指令将CX寄存器和常数0进行比较。如果比较的结果为0,那么下一条JZ为0时跳转指令将跳转到L标号对应的语句,也就是返回变量b的值。如果比较的结果不为0,那么JZ指令将没有效果,继续执行后面的指令,也就是返回变量a的值。

在跳转指令中,跳转的目标一般是通过一个标号表示。不过在有些通过宏实现的函数中,更希望通过相对位置跳转,这时候可以通过PC寄存器的偏移量来计算临近跳转的位置。