@@ -52,7 +52,7 @@ cgo也是一个Go语言自带的特殊工具。一般情况下,我们使用命
52
52
53
53
上面这段代码被保存在了Go语言库源码文件math.go中,并与源码文件rand.go在同一个代码包目录。在Go语言函数```Sqrt```中的```C.sqrt```是一个在C语言标准代码库math.h中的函数。它会返回参数的平方根。但是在第一行代码中,我们接收由函数C.sqrt返回的两个值。其中,第一个值即为C语言函数```sqrt```的结果。而第二个值就是我们上面所说的那个作为错误提示信息的变量。实际上,这个变量的类型是Go语言的```error```接口类型。它包装了一个C语言的全局变量errno。这个全局变量被定义在了C语言代码库errno.h中。cgo工具在为我们生成C语言源码文件时会默认引入两个C语言标准代码库,其中一个就是errno.h。所以我们并不用在Go语言源码文件中使用指令符#include显式的引入这个代码库。cgo工具默认为我们引入的另一个是C语言标准代码库string.h。它包含了很多用于字符串处理和内存处理的函数。
54
54
55
- 在我们以“C.* ”的形式调用C语言代码库时,有一点需要特别注意。在C语言中,如果一个函数的参数是一个具有固定尺寸的数组,那么实际上这个函数所需要的是指向这个数组的第一个元素的指针。C编译器能够正确识别和处理这个调用惯例。它可以自行获取到这个指针并传给函数。但是,这在我们使用cgo工具调用C语言代码库时是行不通的。在Go语言中,我们必须显式的将这个指向数组的第一个元素的指针传递给C语言的函数,像这样:``C.func1(&x[ 0] )` ```。
55
+ 在我们以“C.* ”的形式调用C语言代码库时,有一点需要特别注意。在C语言中,如果一个函数的参数是一个具有固定尺寸的数组,那么实际上这个函数所需要的是指向这个数组的第一个元素的指针。C编译器能够正确识别和处理这个调用惯例。它可以自行获取到这个指针并传给函数。但是,这在我们使用cgo工具调用C语言代码库时是行不通的。在Go语言中,我们必须显式的将这个指向数组的第一个元素的指针传递给C语言的函数,像这样:``` C.func1(&x[0]) ``` 。
56
56
57
57
另一个需要特别注意的地方是,在C语言中没有像Go语言中独立的字符串类型。C语言使用最后一个元素为‘\0’的字符数组来代表字符串。在Go语言的字符串和C语言的字符串之间进行转换的时候,我们就需要用到代码包``` C ``` 中的``` C.C.CString ``` 、``` C.GoString ``` 和``` C.GoStringN ``` 等函数。这些转换操作是通过对字符串数据的拷贝来完成的。Go语言内存管理器并不能感知此类内存分配操作。因为它们是由C语言代码引发的。所以,我们在使用与``` C.CString ``` 函数类似的会导致内存分配操作的函数之后,需要调用代码包C的free函数以手动的释放内存。这里有一个小技巧,即我们可以把对``` C.free ``` 函数的调用放到``` defer ``` 语句中或者放入在defer之后的匿名函数中。这样就可以保证在退出当前函数之前释放这些被分配的内存了。请看下面这个示例:
58
58
@@ -68,7 +68,7 @@ cgo也是一个Go语言自带的特殊工具。一般情况下,我们使用命
68
68
69
69
在前面我们已经提到过,在导入代码包C的语句之上可以加入若干个为cgo工具而写的若干注释行(也被叫做序文)。并且,以#include和一个空格开始的注释行可以用来引入一个C语言的接口文件。我们也把序文中这种形式的字符串叫做指令符。指令符``` #cgo ``` 的用途是为编译器和连接器提供标记。这些标记在编译当前源码文件中涉及到代码包``` C ``` 的那部分代码时会被用到。
70
70
71
- 标记``` CFLAGS ``` 和``` LDFLAGS``可以被放在指令符 ``` #cgo```之后,并用于定制编译器gcc的行为。gcc(GNU Compiler Collection,GNU编译器套装),是一套由GNU开发的开源的编程语言编译器。它是GNU项目的关键部分,也是类Unix操作系统(也包括Linux操作系统)中的标准编译器。gcc(特别是其中的C语言编译器)也常被认为是跨平台编译器的事实标准。gcc原名为GNU C语言编译器(GNU C Compiler),因为它原本只能处理C语言。不过,gcc变得可以处理更多的语言。现在,gcc中包含了很多针对特定编程语言的编译器。我们在本节第一小节的末尾提及的gccgo就是这款套件中针对Go语言的编译器。标记CFLAGS可以指定用于gcc中的C编译器的选项。它尝尝用于指定头文件(.h文件)的路径。而标记LDFLAGS则可以指定gcc编译器会用到的一些优化参数,也可以用来告诉链接器需要用到的C语言代码库文件的位置。
71
+ 标记``` CFLAGS ``` 和``` LDFLAGS ``` 可以被放在指令符``` #cgo ``` 之后,并用于定制编译器gcc的行为。gcc(GNU Compiler Collection,GNU编译器套装),是一套由GNU开发的开源的编程语言编译器。它是GNU项目的关键部分,也是类Unix操作系统(也包括Linux操作系统)中的标准编译器。gcc(特别是其中的C语言编译器)也常被认为是跨平台编译器的事实标准。gcc原名为GNU C语言编译器(GNU C Compiler),因为它原本只能处理C语言。不过,gcc变得可以处理更多的语言。现在,gcc中包含了很多针对特定编程语言的编译器。我们在本节第一小节的末尾提及的gccgo就是这款套件中针对Go语言的编译器。标记CFLAGS可以指定用于gcc中的C编译器的选项。它尝尝用于指定头文件(.h文件)的路径。而标记LDFLAGS则可以指定gcc编译器会用到的一些优化参数,也可以用来告诉链接器需要用到的C语言代码库文件的位置。
72
72
73
73
为了清晰起见,我们可以把这些标记及其值拆分成多个注释行,并均以指令符``` #cgo ``` 作为前缀。另外,在指令符``` #cgo ``` 和标记之间,我们也可以加入一些可选的内容,即环境变量GOOS和GOARCH中的有效值。这样,我们就可以使这些标记只在某些操作系统和/或某些计算架构的环境下起作用了。示例如下:
74
74
@@ -500,8 +500,8 @@ Go语言可以使它的函数被C语言代码所用。这是通过使用一个
500
500
501
501
hc@ubt:~/golang/goc2p/basic/cgo/lib$ go build
502
502
# basic/cgo/lib
503
- /tmp/go-build477634277/basic/cgo/lib/_obj/go_export.cgo2.o: In function ` _cgo_cc103c85817e_Cfunc_CFunction1':
504
- ./go_export.go:34: undefined reference to ` CFunction1'
503
+ /tmp/go-build477634277/basic/cgo/lib/_obj/go_export.cgo2.o: In function ' _cgo_cc103c85817e_Cfunc_CFunction1':
504
+ ./go_export.go:34: undefined reference to ' CFunction1'
505
505
collect2: ld return 1
506
506
507
507
构建并没有成功完成。根据错误提示信息我们获知,C语言函数``` CFunction1 ``` 未被定义。这个问题的原因是我们并没有在Go语言源码文件go_export.go的序文中写入C语言函数``` CFunction1 ``` 的实现,也即未对它进行定义。我们之前说过,在这种情况下,对应函数的定义应该被放到其它Go语言源码文件的序文或者C语言源码文件中。现在,我们在当前目录下创建一个新的Go语言源码文件go_export_def.go。其内容如下:
@@ -528,7 +528,7 @@ Go语言可以使它的函数被C语言代码所用。这是通过使用一个
528
528
529
529
显然,这次的构建成功完成。当然单独构建代码包``` basic/cgo/lib ``` 并不是必须的。我们在这里是为了检查该代码包中的代码(包括Go语言代码和C语言代码)是否都能够被正确编译。
530
530
531
- 还记得goc2p项目的代码包basic/cgo中的命令源码文件cgo_demo.go。现在我们在它的main函数的最后加入一行新代码:``` cgo.CallCFunc() ``` ,即调用在代码包``basic/cgo/lib```中的库源码文件go_export.go的函数。然后,我们运行这个命令源码文件:
531
+ 还记得goc2p项目的代码包basic/cgo中的命令源码文件cgo_demo.go。现在我们在它的main函数的最后加入一行新代码:``` cgo.CallCFunc() ``` ,即调用在代码包``` basic/cgo/lib ``` 中的库源码文件go_export.go的函数。然后,我们运行这个命令源码文件:
532
532
533
533
hc@ubt:~/golang/goc2p/basic/cgo$ go run cgo_demo.go
534
534
The square root of 2.330000 is 1.526434.
@@ -555,4 +555,4 @@ Go语言可以使它的函数被C语言代码所用。这是通过使用一个
555
555
CFunction1() is called.
556
556
GoFunction1() is called.
557
557
558
- 在上例中,我们首先删除了代码包``` basic/cgo/lib ``` 目录下的子目录_obj,以此来保证原始的测试环境。然后,我们直接运行了命令源码文件cgo_demo.go。在这个源码文件中,包含了对代码包``` basic/cgo/lib ``` 中函数的调用语句,而在这些函数中又包含了对代码包C的引用。从输出信息我们可以看出,命令源码文件cgo_demo.go的运行成功的完成了。这也验证了标准go命令在这方面的功能。不过,有时候我们还是很有必要单独使用``` go tool cgo ``` 命令,比如对相关的Go语言代码和C语言代码的功能进行验证或者需要通过标记定制化运行cgo工具的时候。另外,如果我们通过标准go命令构建或者安装直接或间接导入了代码C的命令源码文件,那么在生成的可执行文件中就会包含动态导入数据和动态链接器信息。我们可以使用``` go tool cgo ``` 命令查看可执行文件中的这些信息。
558
+ 在上例中,我们首先删除了代码包``` basic/cgo/lib ``` 目录下的子目录_obj,以此来保证原始的测试环境。然后,我们直接运行了命令源码文件cgo_demo.go。在这个源码文件中,包含了对代码包``` basic/cgo/lib ``` 中函数的调用语句,而在这些函数中又包含了对代码包C的引用。从输出信息我们可以看出,命令源码文件cgo_demo.go的运行成功的完成了。这也验证了标准go命令在这方面的功能。不过,有时候我们还是很有必要单独使用``` go tool cgo ``` 命令,比如对相关的Go语言代码和C语言代码的功能进行验证或者需要通过标记定制化运行cgo工具的时候。另外,如果我们通过标准go命令构建或者安装直接或间接导入了代码C的命令源码文件,那么在生成的可执行文件中就会包含动态导入数据和动态链接器信息。我们可以使用``` go tool cgo ``` 命令查看可执行文件中的这些信息。
0 commit comments