1
0
mirror of https://github.com/chai2010/advanced-go-programming-book.git synced 2025-05-28 15:32:20 +00:00
2018-07-22 18:57:42 +08:00

106 lines
5.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## CGO版本
Go语言开源初期就支持通过CGO和C语言保持交互。CGO通过导入一个虚拟的`"C"`包来访问C语言中的函数。下面是CGO版本的“Hello World”程序
```go
package main
// #include <stdio.h>
// #include <stdlib.h>
import "C"
import "unsafe"
func main() {
msg := C.CString("Hello, World!\n")
defer C.free(unsafe.Pointer(msg))
C.fputs(msg, C.stdout)
}
```
先通过`C.CString`函数将Go语言字符串转为C语言字符串然后调用C语言的`C.fputs`函数向标准输出窗口打印转换后的C字符串。`defer`延迟语句保证程序返回前通过`C.free`释放分配的C字符串。需要注意的是, CGO不支持C语言中的可变参数函数因为Go语言每次函数调用的栈帧大小是固定的而且Go语言中可变参数语法只是切片的一个语法糖而已因此在Go语言中是无法通过CGO访问C语言的`printf`等可变参数函数的。同时CGO只能访问C语言的函数、变量和简单的宏定义常量CGO并不支持访问C++语言的符号C++和C语言符号的名字修饰规则不同CGO采用C语言的名字修饰规则
其实CGO不仅仅用于在Go语言中调用C语言函数还可以用于导出Go语言函数给C语言函数调用。在用Go语言编写生成C语言的静、动态库时也可以用CGO导出对应的接口函数。正是CGO的存在才保证了Go语言和C语言资源的双向互通同时保证了Go语言可以继承C语言已有的庞大的软件资产。
## SWIG版本
Go语言开源初期除了支持通过CGO访问C语言资源外还支持通过SWIG访问C/C++接口。SWIG是从2010年10月04日发布的SWIG-2.0.1版本开始正式支持Go语言的。可以将SWIG看作一个高级的CGO代码自动生成器同时通过生成C语言桥接代码增加了对C++类的支持。下面是SWIG版本的"Hello World"程序:
首先是创建一个`hello.cc`文件,里面有`SayHello`函数用于打印(这里的`SayHello`函数采用C++的名字修饰规则):
```c++
#include <iostream>
void SayHello() {
std::cout << "Hello, World!" << std::endl;
}
```
然后创建一个`hello.swigcxx`文件以SWIG语法导出上面的C++函数`SayHello`:
```swig
%module main
%inline %{
extern void SayHello();
%}
```
然后在Go语言中直接访问`SayHello`函数(首字母自动转为大写字母):
```go
package main
import (
hello "."
)
func main() {
hello.SayHello()
}
```
需要将上述3个文件放到同一个目录中并且`hello.swigcxx`和Go文件对应同一个包。系统除了需要安装Go语言环境外还需要安装对应版本的SWIG工具。最后运行`go build`就可以构建了。
*注: 在Windows系统下, 路径最长为260个字符. 这个程序生成的中间cgo文件可能导致某些文件的绝对路径长度超出Windows系统限制, 可能导致程序构建失败. 这是由于`go build`调用swig和cgo等命令生成中间文件时生成的不合适的超长文件名导致作者提交ISSUE3358Go1.8已经修复)。*
## Go汇编语言版本
Go语言底层使用了自己独有的跨操作系统汇编语言该汇编语言是从Plan9系统的汇编语言演化而来。Go汇编语言并不能独立使用它是属于Go语言的一个组成部分必须以Go语言包的方式被组织。下面是Go汇编语言版本的“Hello World”程序
先创建一个`main.go`文件以Go语言的语法声明包和声明汇编语言对应的函数签名函数签名不能有函数体
```go
package main
func main()
```
然后创建`main_amd64.s`文件对应Go汇编语言实现AMD64架构的`main`函数:
```asm
#include "textflag.h"
#include "funcdata.h"
// "Hello World!\n"
DATA text<>+0(SB)/8,$"Hello Wo"
DATA text<>+8(SB)/8,$"rld!\n"
GLOBL text<>(SB),NOPTR,$16
// func main()
TEXT ·main(SB), $16-0
NO_LOCAL_POINTERS
MOVQ $text<>+0(SB), AX
MOVQ AX, (SP)
MOVQ $16, 8(SP)
CALL runtime·printstring(SB)
RET
```
代码中`#include "textflag.h"`语句包含运行时库定义的头文件, 里面含有`NOPTR`/`NO_LOCAL_POINTERS`等基本的宏的定义。`DATA`汇编指令用于定义数据每个数据的宽度必须是1/2/4/8然后`GLOBL`汇编命令在当前文件内导出`text`变量符号。`TEXT ·main(SB), $16-0`用于定义`main`函数,其中`$16-0`表示`main`函数的帧大小是16个字节对应string头的大小用于给`runtime·printstring`函数传递参数),`0`表示`main`函数没有参数和返回值。`main`函数内部通过调用运行时内部的`runtime·printstring(SB)`函数来打印字符串。
Go汇编语言虽然针对每种CPU架构主要有386/AMD64/ARMARM64等有对应的指令和寄存器但是汇编语言的基本语法和函数调用规范是一致的不同操作系统之间用法是一致的。在Go语言标准库中`runtime`运行时库、`math`数学库和`crypto`密码相关的函数很多是采用汇编语言实现的。其中`runtime`运行时库中采用部分汇编语言并不完全是为了性能而是运行时的某些特性功能比如goroutine上下文的切换等无法用纯Go实现因此需要汇编代码实现某些辅助功能。对于普通用户而言Go汇编语言的最大价值在于性能的优化对于性能比较关键的地方可以尝试用Go汇编语言实现终极优化。