Golang cgo:如何在Go代码中调用C语言代码?

简介: Golang cgo:如何在Go代码中调用C语言代码?

如何在Go代码中调用C语言代码?

Go语言是通过自带的一个叫CGO的工具来支持C语言函数调用,同时我们可以用Go语言导出C动态库接口给其它语言使用。

方式一、直接在 Go 代码中写入 C 代码

检查是否开启cgo工具

首先,要查看是否已经开启cgo工具:go env | findstr CGO。若是0则为关闭状态,需要手动开启:set CGO_ENABLED=1
在这里插入图片描述

编写main.go文件

随后,新建一个 main.go 文件,在注释中写入 C 语言的代码,并且需要导入 Go 提供的 C 包 import "C"。Go 语言在看到导入这个包之后就知道如何去识别和处理注释中的 C 语言代码了,具体示例如下:

$\color{red}{注意C 代码和import "c"​​ 语句之间不能有空行 !}$

package main

// #include <stdio.h>
// #include <stdlib.h>
/*
    void print_fun(char *s) {
        printf("print used by C language: %s\n", s);
    }
*/
import "C"

func main() {
    cStr := C.CString("hello world !") // golang 操作c 标准库中的CString函数
    C.print_fun(cStr)                  // 调用C函数:print_fun 打印输出
}

在这里插入图片描述
这种方式的 C 代码和 Go 代码在同一个文件中,所以大家能明显发现这种方式的代码耦合度太高,仅适用于项目简单单一的情形。

一个更合理的方式应该是 C 代码单独在一个文件。


方式二、Go 直接调用 C 文件

那么,如果已经写好一个封装好的 C 文件代码,Go 语言该如何调用呢?

此时我们需要直接写好 C 相关代码,因为 Go 代码是无法对 C 代码文件进行重写或者修改的。

以如下文件目录结构,创建相关 .h.c.go文件:
在这里插入图片描述

C语言 .h 头文件
  • 编写 ​​hello.h​​ 头文件,示例代码如下:
#ifndef HELLO_H
#define HELLO_H

void print_fun1(char *s);

#endif
C语言 .c 源文件

编写 ​​hello.c​​ 源文件,示例代码如下:

#include "hello.h"
#include <stdio.h>

void print_fun1(char *s) {
    printf("print used by C language: %s\n", s);
}
Go语言 .go 文件

最后编写 ​​main.go​​ 文件:

  • 我们需要在 CFLAGS 参数中填入我们的 GOPath 路径, ​​#cgo CFLAGS: -I xxx​​,xxx这里也可以填写 绝对路径/相对路径
  • 然后在 LDFLAGS 中填入我们的 C 编译后的本地链接文件: ​​#cgo LDFLAGS: xxx/hello.a​​,xxx这里也可以填写 绝对路径/相对路径

$\color{red}{注意C 代码和import "c"​​ 语句之间不能有空行 !}$

具体示例如下:

package main

/*
#cgo CFLAGS: -I .
#cgo LDFLAGS: ./hello.a
#include <stdlib.h>
#include <hello.h>
*/
import "C"

import (
    "unsafe"
)

func main() {
    cStr := C.CString("hello world !") // golang 操作c 标准库中的CString函数
    C.print_fun1(cStr)                 // 调用C函数:print_fun 打印输出

    defer C.free(unsafe.Pointer(cStr)) // 因为 CSstring 这个函数没有对变量申请的空间进行内存释放
}

在这里插入图片描述

编写完以上文件后,距离可运行的程序只有一步之遥了。

编译 c 代码
$ gcc -c *.c           # 生成.o文件

$ ar rs hello.a *.o # 生成.a文件
ar: creating archive hello.a

$ rm ./hello.o         # 删除已不需要的.o文件,linux下为rm命令,windows下为del命令

生成.a文件需要用到 ar 命令建立或修改备存文件,可参考:Linux ar命令

执行 go 代码
go run main.go

补充

使用了 cStr := C.CString("hello world !"),记得调用C.free(unsafe.Pointer(cStr))释放当前变量申请的内存空间。

可参考:[golang 操作c 标准库中的CString函数注意事项
](https://blog.csdn.net/liangguangchuan/article/details/52920054)

目录
相关文章
|
26天前
|
存储 编译器 C语言
【数据结构】C语言实现链队列(附完整运行代码)
【数据结构】C语言实现链队列(附完整运行代码)
36 0
|
16小时前
|
安全 Go 开发者
Golang深入浅出之-Go语言并发编程面试:Goroutine简介与创建
【4月更文挑战第22天】Go语言的Goroutine是其并发模型的核心,是一种轻量级线程,能低成本创建和销毁,支持并发和并行执行。创建Goroutine使用`go`关键字,如`go sayHello(&quot;Alice&quot;)`。常见问题包括忘记使用`go`关键字、不正确处理通道同步和关闭、以及Goroutine泄漏。解决方法包括确保使用`go`启动函数、在发送完数据后关闭通道、设置Goroutine退出条件。理解并掌握这些能帮助开发者编写高效、安全的并发程序。
10 1
|
20小时前
|
SQL 关系型数据库 MySQL
Golang数据库编程详解 | 深入浅出Go语言原生数据库编程
Golang数据库编程详解 | 深入浅出Go语言原生数据库编程
|
23小时前
|
存储 算法 C语言
C语言进阶:顺序表(数据结构基础) (以通讯录项目为代码练习)
C语言进阶:顺序表(数据结构基础) (以通讯录项目为代码练习)
|
1天前
|
Go 开发者
Golang深入浅出之-Go语言流程控制:if、switch、for循环详解
【4月更文挑战第21天】本文介绍了Go语言中的流程控制语句,包括`if`、`switch`和`for`循环。`if`语句支持简洁的语法和初始化语句,但需注意比较运算符的使用。`switch`语句提供多分支匹配,可省略`break`,同时支持不带表达式的形式。`for`循环有多种形式,如基本循环和`for-range`遍历,遍历时修改原集合可能导致未定义行为。理解并避免易错点能提高代码质量和稳定性。通过实践代码示例,可以更好地掌握Go语言的流程控制。
8 3
Golang深入浅出之-Go语言流程控制:if、switch、for循环详解
|
1天前
|
Go
Golang深入浅出之-Go语言函数基础:定义、调用与多返回值
【4月更文挑战第21天】Go语言函数是代码组织的基本单元,用于封装可重用逻辑。本文介绍了函数定义(包括基本形式、命名、参数列表和多返回值)、调用以及匿名函数与闭包。在函数定义时,注意参数命名和注释,避免参数顺序混淆。在调用时,要检查并处理多返回值中的错误。理解闭包原理,小心处理外部变量引用,以提升代码质量和可维护性。通过实践和示例,能更好地掌握Go语言函数。
14 1
Golang深入浅出之-Go语言函数基础:定义、调用与多返回值
|
2天前
|
Go
Golang深入浅出之-Go数据类型详解:整型、浮点型与布尔型
【4月更文挑战第20天】Go语言基础数据类型包括整型(有符号和无符号,如`int8`、`uint32`)、浮点型(`float32`、`float64`)和布尔型(`true`、`false`)。理解它们的范围和特性,以及注意溢出、精度损失、类型转换等问题,是编写高效Go代码的关键。例如,整型溢出可能导致模运算,浮点数比较可能有精度误差,布尔型不应用于数值计算。了解这些易错点,能帮助写出更健壮的代码。
17 0
|
2天前
|
Go
Golang深入浅出之-Go语言基础语法:变量声明与赋值
【4月更文挑战第20天】本文介绍了Go语言中变量声明与赋值的基础知识,包括使用`var`关键字和简短声明`:=`的方式,以及多变量声明与赋值。强调了变量作用域、遮蔽、初始化与零值的重要性,并提醒读者注意类型推断时的一致性。了解这些概念有助于避免常见错误,提高编程技能和面试表现。
18 0
|
22天前
费马螺线在现实生活中的应用
费马螺线在现实生活中的应用
10 1
|
6月前
|
存储 编译器 Go
Golang 语言的多种变量声明方式和使用场景
Golang 语言的多种变量声明方式和使用场景
32 0