
本文将深入探讨如何从 C 代码中调用 Go 函数,这在构建混合系统时非常有用。通过代码示例,详细解释了如何在 Go 中定义可供 C 调用的函数,以及如何在 C 代码中调用这些 Go 函数。此外,文章还提供了一个更高级的示例,展示如何使用回调函数处理 C 代码中的异步事件,并最终提供一个完整的、可运行的例子,帮助开发者理解并实践这一技术。
从 C 代码中调用 Go 函数涉及到使用 cgo 工具,它是 Go 的一部分,允许 Go 代码与 C 代码进行互操作。cgo 能够生成必要的桥接代码,使得 C 代码可以调用 Go 函数,反之亦然。其核心在于 //export 注释和 extern 声明,它们分别用于在 Go 代码中标记可导出的函数,以及在 C 代码中声明这些函数。
以下是一个简单的示例,演示了如何从 C 代码中调用 Go 函数执行加法运算:
package main
// #include <stdio.h>
// #include <stdlib.h>
//
// extern int goCallbackHandler(int, int);
//
// static int doAdd(int a, int b) {
// return goCallbackHandler(a, b);
// }
import "C"
import "fmt"
//export goCallbackHandler
func goCallbackHandler(a, b C.int) C.int {
fmt.Printf("Go: Received %d and %d from C\n", a, b)
return a + b
}
// MyAdd is the public function, callable from outside this package.
// It forwards the parameters to C.doAdd(), which in turn forwards
// them back to goCallbackHandler(). This one performs the addition
// and yields the result.
func MyAdd(a, b int) int {
return int( C.doAdd( C.int(a), C.int(b)) )
}
func main() {
result := MyAdd(5, 3)
fmt.Printf("Go: Result from C call: %d\n", result)
}在这个例子中:
要编译和运行此示例,你需要创建一个 C 文件(例如 main.c),并在其中调用 MyAdd 函数。然后,使用 go build 命令编译 Go 代码,并使用 C 编译器编译 C 代码,并将它们链接在一起。
一个更高级的用例是在 C 代码中使用回调函数来处理异步事件。例如,假设你有一个 C 库,它从 USB 设备读取文件列表,并且你想在 Go 代码中显示读取进度。你可以通过传递一个回调函数指针给 C 库来实现这一点。
package main
// #include <stdio.h>
// #include <stdlib.h>
// #include <stdint.h>
//
// typedef void (*ProgressHandler)(uint64_t current, uint64_t total, void* userdata);
//
// extern int goProgressCB(uint64_t current, uint64_t total, void* userdata);
//
// static int simulate_get_files(ProgressHandler progress_cb, void* userdata) {
// uint64_t total_files = 100;
// for (uint64_t i = 0; i < total_files; i++) {
// if (progress_cb != NULL) {
// progress_cb(i, total_files, userdata);
// }
// // Simulate some work
// for (int j = 0; j < 100000; j++);
// }
// return 0;
// }
//
// static int goGetFiles(void* userdata) {
// return simulate_get_files((ProgressHandler)goProgressCB, userdata);
// }
import "C"
import (
"fmt"
"unsafe"
)
// ProgressHandler defines the signature of our user's progress handler.
type ProgressHandler func(current, total uint64, userdata interface{}) int
// progressRequest is an internal type which will pack the user's callback function and userdata.
type progressRequest struct {
f ProgressHandler // The user's function pointer
d interface{} // The user's userdata.
}
//export goProgressCB
func goProgressCB(current, total C.uint64_t, userdata unsafe.Pointer) C.int {
req := (*progressRequest)(userdata)
return C.int(req.f(uint64(current), uint64(total), req.d))
}
// GetFiles is our public function, which is called by the user.
func GetFiles(pf ProgressHandler, userdata interface{}) int {
req := unsafe.Pointer(&progressRequest{pf, userdata})
return int(C.goGetFiles(req))
}
func main() {
// We call GetFiles. Pass it our progress handler and some arbitrary userdata.
ret := GetFiles(myProgress, "Callbacks rock!")
fmt.Printf("Go: GetFiles returned: %d\n", ret)
}
// myProgress is our progress handler.
func myProgress(current, total uint64, userdata interface{}) int {
fc := float64(current)
ft := float64(total) * 0.01
fmt.Printf("%s: %d / %d (%3.2f%%)\n", userdata.(string), current, total, fc/ft)
return 0
}在这个例子中:
从 C 代码中调用 Go 函数是一种强大的技术,可以让你利用 Go 的优势来扩展现有的 C 代码库。但是,它也需要仔细的规划和实施,以避免潜在的问题。通过理解 cgo 的工作原理,并遵循上述注意事项,你可以成功地构建混合系统,并将 C 和 Go 的优势结合起来。
以上就是在 Go 中调用 C 函数:构建双向桥梁的完整指南的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号