🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
### 在单独的文件用 Go 调用 C 代码 现在让我们继续讨论当 C 代码位于单独的文件中时如何从 Go 程序中调用 C 代码。 首先,我将解释用程序解决的假想的问题。然后我们需要使用已经实现好的两个 C 函数,这些函数我们不希望或无法在 Go 中重写。 #### C 代码部分 本小节将为你提供示例的 C 代码。它有两个文件:`callC.h`和`callC.c`。文件`(callC.h)`包含以下代码: ```c #ifndef CALLC_H #define CALLC_H void cHello(); void printMessage(char* message); #endif ``` 文件`(callC.c)`包含以下代码: ```c #include <stdio.h> #include "callC.h" void cHello() { printf("Hello from C!\n"); } void printMessage(char* message) { printf("Go send me %s\n", message); } ``` `callC.c`和`callC.h`文件都存储在单独的目录中,我们把该目录命名为`callClib`。但是,你可以使用你想使用的任何目录名称。 > Tip: 只要你使用正确的类型和数量的参数调用正确的 C 函数,那么的 C 代码的具体实现并不重要。C 代码中没有任何内容告诉我们它将在 Go 程序中使用。You should look at the Go code for the juicy part. #### Go 代码部分 本小节将为你提供示例的 Go 源代码,该源代码名为`callC.go`,并将分三部分向你介绍。 第一部分的代码: ```Go package main // #cgo CFLAGS: -I${SRCDIR}/callClib // #cgo LDFLAGS: ${SRCDIR}/callC.a // #include <stdlib.h> // #include <callC.h> import "C" ``` 整个 Go 源文件中最重要的 Go 语句是使用单独的 `import`语句包含 C 包。但是,`C`是一个虚拟的 Go 包,它只是告诉`go build`在 go 编译器处理文件之前使用 `cgo` 工具对其输入文件进行预处理。你仍然可以看到你需要使用注释来告知 Go 程序有关 C 代码的信息。在这种情况下,你告诉`callC.go`在哪里可以找到`callC.h`文件以及在哪里可以找到我们将在一段时间内创建的`callC.a`库文件,就像以`#cgo`开头的一段代码。 第一部分的代码: ```Go import ( "fmt" "unsafe" ) func main() { fmt.Println("Going to call a C function!") C.cHello() ``` 最后一部分代码: ```Go fmt.Println("Going to call another C function!") myMessage := C.CString("This is Mihalis!") defer C.free(unsafe.Pointer(myMessage)) C.printMessage(myMessage) fmt.Println("All perfectly done!") } ``` 为了从 Go 将字符串传递给 C 函数,你将需要使用`C.CString()`创建一个 C 字符串。此外,你将需要一个`defer`语句,以便在不再需要 C 字符串时释放其存储空间。 defer 语句包括对`C.free()`和`unsafe.Pointer()`的调用。 在下一部分中,你将看到如何编译和执行`callC.go`。 #### 混合 Go 和 C 代码 现在你已经有了 C 代码和 Go 代码,现在该学习下一步以执行调用 C 代码的 Go 文件了。 由于所有的关键信息都包含在 Go 文件中了,所以你唯一需要做的就是编译 C 代码以生成一个库: ```shell $ ls -l callClib/ total 16 -rw-r--r--@ 1 mtsouk staff 162 Jan 10 09:17 callC.c -rw-r--r--@ 1 mtsouk staff 89 Jan 10 09:17 callC.h $ gcc -c callClib/*.c $ ls -l callC.o -rw-r--r-- 1 mtsouk staff 952 Jan 22 22:03 callC.o $ file callC.o callC.o: Mach-O 64-bit object x86_64 $ /usr/bin/ar rs callC.a *.o ar: creating archive callC.a $ ls -l callC.a -rw-r--r-- 1 mtsouk staff 4024 Jan 22 22:03 callC.a $ file callC.a callC.a: current ar archive $ rm callC.o ``` 之后,你将在与 callC.go 文件相同的目录中拥有一个名为`callC.a`的文件。 gcc 可执行命令是**C 编译器**的名称。 现在,你可以使用 Go 代码编译文件创建一个新的可执行文件: ```shell $ go build callC.go $ ls -l callC -rwxr-xr-x 1 mtsouk staff 2403184 Jan 22 22:10 callC $ file callC callC: Mach-O 64-bit executable x86_64 ``` 执行`callC`可执行文件,你将得到输出: ```shell $ ./callC Going to call a C function! Hello from C! Going to call another C function! Go send me This is Mihalis! All perfectly done! ``` > Tip: 如果要调用少量的 C 代码,强烈建议 C 和 Go 代码在单个 Go 文件中。但是,如果你要进行更复杂和更高级的操作,则首选静态 C 库。