虽然 Go 语言旨在提升编程体验,减少 C 语言的复杂性,但 C 语言依然是非常强大的编程语言,并且在很多情况下依然十分有用。比如在使用数据库或设备驱动程序时,它们可能是用 C 语言编写的。这意味着在某些情况下,你可能需要在 Go 项目中调用 C 代码。
最简单的调用 C 代码的方式是将 C 代码直接包含在 Go 源文件中。虽然这需要一些特殊处理,但这种方式速度很快,也不算太复杂。不过,如果你在同一个项目中多次使用这种功能,可能需要重新考虑这种方式是否合适,或者是否应该换一种编程语言。
下面展示了一个包含 C 代码和 Go 代码的文件 cGo.go
,它分为三部分:
package main // #include <stdio.h> // void callC() { // printf("调用 C 代码!\n"); // } import "C" import "fmt" func main() { fmt.Println("这是 Go 语言中的一条语句!") C.callC() fmt.Println("这是另一条 Go 语言中的语句!") }
在上面的代码中,callC()
C 函数通过 C.callC()
调用。运行该代码会产生以下输出:
$ go run cGo.go 这是 Go 语言中的一条语句! 调用 C 代码! 这是另一条 Go 语言中的语句!
接下来讨论如何在 C 代码位于单独的文件时,从 Go 程序中调用它。假设我们有两个已经编写好的 C 函数,不想在 Go 中重写它们。
首先,C 代码包含在两个文件中:callC.h
和 callC.c
。
callC.h
文件内容如下:
#ifndef CALLC_H #define CALLC_H void cHello(); void printMessage(char* message); #endif
callC.c
文件内容如下:
#include <stdio.h> #include "callC.h" void cHello() { printf("来自 C 语言的问候!\n"); } void printMessage(char* message) { printf("Go 语言发送的消息是: %s\n", message); }
这两个文件存储在 callClib
目录下。当然,你也可以使用任何其他目录名称。
接下来是 Go 源代码,它被命名为 callC.go
,分为三部分展示:
package main // #cgo CFLAGS: -I${SRCDIR}/callClib // #cgo LDFLAGS: ${SRCDIR}/callC.a // #include <stdlib.h> // #include <callC.h> import "C" import ( "fmt" "unsafe" ) func main() { fmt.Println("即将调用 C 函数!") C.cHello() fmt.Println("即将调用另一个 C 函数!") myMessage := C.CString("这是来自 Mihalis 的消息!") defer C.free(unsafe.Pointer(myMessage)) C.printMessage(myMessage) fmt.Println("调用完成!") }
在 Go 中传递字符串给 C 函数时,需要使用 C.CString()
创建一个 C 字符串,同时要使用 defer
语句来确保不再需要时释放内存。
当你编写好 C 代码和 Go 代码后,接下来就是编译并执行这些文件。幸运的是,所有关键信息都已经包含在 Go 文件中,唯一的重点是编译 C 代码以创建一个静态库。你可以执行以下命令来完成这些操作:
$ 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 $ /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
完成这些步骤后,callC.a
文件将位于与 callC.go
文件相同的目录中。此时你可以编译 Go 代码:
$ go build callC.go $ ls -l callC -rwxr-xr-x 1 mtsouk staff 2403184 Jan 22 22:10 callC
执行生成的可执行文件:
$ ./callC 即将调用 C 函数! 来自 C 语言的问候! 即将调用另一个 C 函数! Go 语言发送的消息是: 这是来自 Mihalis 的消息! 调用完成!
如果你只需要调用少量的 C 代码,那么在同一个 Go 文件中同时包含 C 和 Go 代码是一个简洁的选择。然而,当涉及到更复杂的项目时,创建一个静态的 C 库可能是更好的选择。
- EOF -