在 Go 语言中调用 C 代码的技巧
虽然 Go 语言旨在提升编程体验,减少 C 语言的复杂性,但 C 语言依然是非常强大的编程语言,并且在很多情况下依然十分有用。比如在使用数据库或设备驱动程序时,它们可能是用 C 语言编写的。这意味着在某些情况下,你可能需要在 Go 项目中调用 C 代码。
在同一个文件中调用 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 代码
接下来讨论如何在 C 代码位于单独的文件时,从 Go 程序中调用它。假设我们有两个已经编写好的 C 函数,不想在 Go 中重写它们。
C 代码
首先,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 代码
接下来是 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
语句来确保不再需要时释放内存。
编译和执行混合的 Go 和 C 代码
当你编写好 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 库可能是更好的选择。