0

0

在 Go 中调用 C 函数的进阶教程:实现回调机制

心靈之曲

心靈之曲

发布时间:2025-07-31 19:04:10

|

833人浏览过

|

来源于php中文网

原创

在 go 中调用 c 函数的进阶教程:实现回调机制

本文深入探讨了如何在 Go 语言中调用 C 函数,并重点介绍了实现 C 函数回调 Go 函数的复杂过程。通过详细的代码示例和步骤说明,我们将展示如何利用 cgo 工具,在 Go 代码中定义和使用 C 函数,并实现从 C 代码到 Go 代码的回调,从而构建更强大的跨语言应用。

使用 cgo 在 Go 中调用 C 函数

cgo 是 Go 语言提供的一个强大的工具,它允许我们在 Go 代码中无缝地调用 C 代码。这对于利用现有的 C 库或与 C 语言编写的系统进行交互非常有用。

基本步骤:

  1. 导入 "C" 包: 在 Go 代码中,首先需要导入 "C" 包。

  2. 包含 C 头文件: 在导入 "C" 包之前的注释中,可以使用 #include 指令包含 C 头文件,声明需要调用的 C 函数。

  3. 调用 C 函数: 通过 C. 前缀调用 C 函数。需要注意的是,Go 和 C 的数据类型不同,需要在调用时进行类型转换。

示例:

package main

/*
#include 
#include 

int add(int a, int b) {
    return a + b;
}
*/
import "C"
import "fmt"

func main() {
    a := C.int(10)
    b := C.int(20)
    result := C.add(a, b)
    fmt.Println("Result:", result)
}

在这个例子中,我们定义了一个简单的 C 函数 add,并在 Go 代码中调用它。

实现 C 函数回调 Go 函数

从 C 函数回调 Go 函数稍微复杂一些,需要使用 //export 注释和 unsafe 包。

核心概念:

  1. //export 注释: 需要在 Go 函数定义前添加 //export 注释,以便 cgo 工具能够生成相应的 C 代码,使得 C 代码可以调用该 Go 函数。

    TURF(开源)权限管理系统
    TURF(开源)权限管理系统

    TURF(开源)权限定制管理系统(以下简称“TURF系统”),是蓝水工作室推出的一套基于软件边界设计理念研发的具有可定制性的权限管理系统。TURF系统充分考虑了易用性,将配置、设定等操作进行了图形化设计,完全在web界面实现,程序员只需在所要控制的程序中简单调用一个函数,即可实现严格的程序权限管控,管控力度除可达到文件级别外,还可达到代码级别,即可精确控制到

    下载
  2. extern 声明: 在 C 代码中,需要使用 extern 关键字声明 Go 函数,以便 C 代码知道该函数的存在。

  3. 类型转换: 由于 Go 和 C 的数据类型不同,需要在回调时进行类型转换。

  4. unsafe 包: 在回调过程中,可能需要使用 unsafe 包进行指针转换。

示例:

package main

/*
#include 
#include 

extern int goCallback(int a, int b);

int callGoCallback(int a, int b) {
    return goCallback(a, b);
}
*/
import "C"
import "fmt"

//export goCallback
func goCallback(a C.int, b C.int) C.int {
    fmt.Println("Go callback called with:", a, b)
    return a + b
}

func main() {
    a := C.int(5)
    b := C.int(7)
    result := C.callGoCallback(a, b)
    fmt.Println("Result:", result)
}

在这个例子中,我们定义了一个 Go 函数 goCallback,并使用 //export 注释将其导出。然后在 C 代码中声明该函数,并通过 callGoCallback 函数调用它。

更高级的回调:传递用户数据

有时候,我们需要在回调函数中传递一些用户自定义的数据。可以使用 void* 指针在 C 代码中传递任意类型的数据,并在 Go 代码中使用 unsafe.Pointer 接收。

package main

/*
#include 
#include 

typedef struct {
    int id;
    char* name;
} UserData;

extern int goProgressCB(unsigned long long current, unsigned long long total, void* userdata);

int callGoProgressCB(unsigned long long current, unsigned long long total, void* userdata) {
    return goProgressCB(current, total, userdata);
}
*/
import "C"
import "fmt"
import "unsafe"

type ProgressHandler func(current, total uint64, userdata interface{}) int

type progressRequest struct {
    f ProgressHandler // The user's function pointer
    d interface{}     // The user's userdata.
}

//export goProgressCB
func goProgressCB(current C.ulonglong, total C.ulonglong, userdata unsafe.Pointer) C.int {
    // This is the function called from the C world by our expensive
    // C.somelib_get_files() function. The userdata value contains an instance
    // of *progressRequest, We unpack it and use it's values to call the
    // actual function that our user supplied.
    req := (*progressRequest)(userdata)

    // Call req.f with our parameters and the user's own userdata value.
    return C.int(req.f(uint64(current), uint64(total), req.d))
}

func GetFiles(current, total uint64, pf ProgressHandler, userdata interface{}) int {
    // Instead of calling the external C library directly, we call our C wrapper.
    // We pass it the handle and an instance of progressRequest.

    req := unsafe.Pointer(&progressRequest{pf, userdata})
    return int(C.callGoProgressCB(C.ulonglong(current), C.ulonglong(total), req))
}

func main() {
    myProgress := func(current, total uint64, userdata interface{}) int {
        fc := float64(current)
        ft := float64(total) * 0.01

        // print how far along we are.
        // eg: 500 / 1000 (50.00%)
        // For good measure, prefix it with our userdata value, which
        // we supplied as "Callbacks rock!".
        fmt.Printf("%s: %d / %d (%3.2f%%)\n", userdata.(string), current, total, fc/ft)
        return 0
    }

    GetFiles(500, 1000, myProgress, "Callbacks rock!")
}

在这个例子中,我们定义了一个 UserData 结构体,并在 C 代码中将其作为 void* 指针传递给 Go 函数 goCallback。在 goCallback 函数中,我们将 unsafe.Pointer 转换为 *UserData,并访问其成员。

注意事项

  • 内存管理: 需要特别注意 C 和 Go 之间的内存管理。C 代码分配的内存需要手动释放,否则会导致内存泄漏。Go 代码分配的内存,如果传递给 C 代码,也需要确保在 C 代码不再使用后释放。
  • 数据类型转换: Go 和 C 的数据类型不同,需要在调用时进行类型转换。
  • 错误处理: C 代码的错误需要传递给 Go 代码进行处理。
  • 线程安全: 在多线程环境下,需要考虑线程安全问题。
  • cgo 的开销: 使用 cgo 会带来一定的性能开销,需要在性能敏感的场景下进行评估。

总结

通过 cgo 工具,我们可以在 Go 语言中方便地调用 C 代码,并实现 C 函数回调 Go 函数的功能。这为我们构建更强大的跨语言应用提供了便利。但是,在使用 cgo 时,需要特别注意内存管理、数据类型转换、错误处理和线程安全等问题。希望本文能够帮助你更好地理解和使用 cgo,构建更高效、可靠的跨语言应用。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
数据类型有哪几种
数据类型有哪几种

数据类型有整型、浮点型、字符型、字符串型、布尔型、数组、结构体和枚举等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

309

2023.10.31

php数据类型
php数据类型

本专题整合了php数据类型相关内容,阅读专题下面的文章了解更多详细内容。

222

2025.10.31

golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

220

2025.06.09

golang结构体方法
golang结构体方法

本专题整合了golang结构体相关内容,请阅读专题下面的文章了解更多。

192

2025.07.04

javascriptvoid(o)怎么解决
javascriptvoid(o)怎么解决

javascriptvoid(o)的解决办法:1、检查语法错误;2、确保正确的执行环境;3、检查其他代码的冲突;4、使用事件委托;5、使用其他绑定方式;6、检查外部资源等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

177

2023.11.23

java中void的含义
java中void的含义

本专题整合了Java中void的相关内容,阅读专题下面的文章了解更多详细内容。

98

2025.11.27

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

503

2023.08.10

Python 多线程与异步编程实战
Python 多线程与异步编程实战

本专题系统讲解 Python 多线程与异步编程的核心概念与实战技巧,包括 threading 模块基础、线程同步机制、GIL 原理、asyncio 异步任务管理、协程与事件循环、任务调度与异常处理。通过实战示例,帮助学习者掌握 如何构建高性能、多任务并发的 Python 应用。

166

2025.12.24

俄罗斯Yandex引擎入口
俄罗斯Yandex引擎入口

2026年俄罗斯Yandex搜索引擎最新入口汇总,涵盖免登录、多语言支持、无广告视频播放及本地化服务等核心功能。阅读专题下面的文章了解更多详细内容。

158

2026.01.28

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
React 教程
React 教程

共58课时 | 4.2万人学习

Pandas 教程
Pandas 教程

共15课时 | 1.0万人学习

ASP 教程
ASP 教程

共34课时 | 4.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号