详解如何在Go语言中调用C源代码

 更新时间:2022年05月07日 15:38:10   作者:神王攻大人  
这篇文章主要为大家详细介绍了如何在Go语言中调用C语言源代码,文中的示例代码讲解详细,对我们学习或工作有一定的帮助,需要的可以参考一下

开坑说明

最近在编写客户端程序或与其他部门做功能集成时多次碰到了跨语言的sdk集成,虽说方案很多诸如rpc啊,管道啊,文件io啊,unix socket啊之类的不要太多,但最完美的基础方式还是让程序与sdk结合到一起(个人观点,不喜勿喷),顺便研究了下在go调用标准c接口的种种方法与坑,内容不少,有空便慢慢更新了。

内嵌形式

先让我们来看一个最简单的cgo实例

package main

//#include <stdio.h>
import "C"

func main() {
	C.puts(C.CString("Hello World"))
}

输出

Hello World

通过"C包"调用了c中常见的puts函数同时传入通过C.Cstring把go 中string转化为的c string(相当于char *)。其实“C”这个并不是一个包,而是通过import "C"语句启用了go编译器cgo相关的功能让gcc也参与到了编译中。这种方式通过紧贴在import "C"语句上面的注释中编写c代码并在后续代码中使用C对象调用。当然也可以通过这种方式调用自定义的c函数。

package main
import "C"

/*#include <stdio.h>

void say_hello_with_name(char * name){
	printf("hello %s\n", name);
}
 */
import "C"

func main() {
	C.say_hello_with_name(C.CString("oscar"))
}

输出

hello oscar

外置的C代码

内置的C代码固然很方便,但用到cgo大多数的使用场景是我有一个需要复用的c代码库,像是c++的stl库亦或者是linux c中的什么已经封装好的第三方依赖。这些时候便需要外置一些c的文件.h .c .cpp之类与.go文件混编。先看一个最简单的例子(调用linux的系统账户认证)。

// auth.h
int auth(char *user, char *passwd);
// auth.c
#include <shadow.h>
#include <stdio.h>
#include <unistd.h>

int auth(char *user, char *passwd){
    char *obtpwd;
    struct spwd *spasswd;

    spasswd = getspnam(user);
    obtpwd = crypt(passwd, spasswd->sp_pwdp);
    if(strcmp(spasswd->sp_pwdp, obtpwd) == 0)
    return 0;
    else return 1;
}
// main.go
package main

/*
#cgo LDFLAGS: -lcrypt

#include "auth.h"
*/
import "C"
import "fmt"

func main() {
	var username, password string

	fmt.Println("Please enter your username and password: ")
    _, _ = fmt.Scanln(&username, &password)

	rst := C.auth(C.CString(username), C.CString(password))
	fmt.Println(rst)
}

保证上述三个文件在同一个go工程目录下运行 go build -o main 构建工程。#cgo LDFLAGS: -lcrypt 这个一行是cgo给gcc的编译参数,相关的编译参数与连接参数有空了在后面的文章里说明,-lcrypt 表示编译时需要去连接libcrypt这个库。
注意,这种c go 混编的方式个人是不建议的,cgo对外置c代码片构建支持非常差,我无法在cgo中通过编译参数指定c代码片的搜索路径(头文件倒是没啥问题),这也就意味着当项目被调用的c代码片都得在项目根目录下,这可太糟糕了。个人觉得如果有大量的外部依赖c语言的库请分开编译,c库使用gcc编译成静态或动态库在让go在编译时连接为好,写个makefile分开分步编译也不是什么麻烦事,还是上面的例子,让我们把编译的过程稍加修改。

1. 构建libauth.a静态库

gcc -c -o auth.o -lcrypt auth.c
ar rcs libauth.a auth.o

得到libauth.a

2. 对main.go稍加修改

package main

/*
#cgo CFLAGS: -I./
#cgo LDFLAGS: -L. -lauth -lcrypt

#include "auth.h"
*/
import "C"
import "fmt"

func main() {
	var username, password string

	fmt.Println("Please enter your username and password: ")
    _, _ = fmt.Scanln(&username, &password)

	rst := C.auth(C.CString(username), C.CString(password))
	fmt.Println(rst)
}

此处修改主要是新增了libauth.a静态库的链接参数

3. 编译

go build -o main main.go

可以把上述的步骤整下写个简单的makefile

.PHONY : all

all: main

libauth.a: auth.c
	gcc -c -o auth.o -lcrypt auth.c
	ar rcs libauth.a auth.o

main: main.go libauth.a
	go build -o main main.go

clean:
	rm -f auth.o libauth.a main

这样也让我们得出产物的过程变得相对简单快捷

到此这篇关于详解如何在Go语言中调用C源代码的文章就介绍到这了,更多相关Go调用C源代码内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 示例剖析golang中的CSP并发模型

    示例剖析golang中的CSP并发模型

    这篇文章主要为大家介绍了示例剖析golang中的CSP并发模型,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-05-05
  • goland 清除所有的默认设置操作

    goland 清除所有的默认设置操作

    这篇文章主要介绍了goland 清除所有的默认设置操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-04-04
  • Golang极简入门教程(四):编写第一个项目

    Golang极简入门教程(四):编写第一个项目

    这篇文章主要介绍了Golang极简入门教程(四):编写第一个项目,本文讲解了workspace、包路径、第一个可执行命令等内容,需要的朋友可以参考下
    2014-10-10
  • Golang学习笔记之安装Go1.15版本(win/linux/macos/docker安装)

    Golang学习笔记之安装Go1.15版本(win/linux/macos/docker安装)

    这篇文章主要介绍了Golang学习笔记之安装Go1.15版本(win/linux/macos/docker安装),本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-12-12
  • 揭秘Go语言中的反射机制

    揭秘Go语言中的反射机制

    在Go语言中,反射是通过reflect包来实现的,通过使用反射,我们可以在运行时获取对象的类型信息、访问对象的字段和方法、动态调用方法等,反射在很多场景下都非常有用,比如编写通用的代码、实现对象的序列化和反序列化、实现依赖注入等,需要的朋友可以参考下
    2023-10-10
  • Go语言利用接口实现链表插入功能详解

    Go语言利用接口实现链表插入功能详解

    这篇文章主要为大家介绍了Go语言中的接口,以及如何利用接口实现链表插入功能,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一下
    2022-04-04
  • 浅谈Go语言中的次方用法

    浅谈Go语言中的次方用法

    这篇文章主要介绍了浅谈Go语言中的次方用法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • Golang利用Recover进行错误处理

    Golang利用Recover进行错误处理

    Golang 中的 recover 是一个鲜为人知但非常有趣和强大的功能,这篇文章小编就来带大家深入了解一下在Golang中是如何利用Recover进行错误处理吧
    2023-12-12
  • 详解Golang中interface{}的注意事项

    详解Golang中interface{}的注意事项

    学习 golang ,对于 interface{} 接口类型,我们一定绕不过,这篇文章咱们就来一起来看看 使用 interface{} 的时候,都有哪些注意事项吧
    2023-03-03
  • 详细介绍Go语言之数组与切片

    详细介绍Go语言之数组与切片

    这篇文章介绍Go语言之数组与切片,数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可是任意的原始类型如整形、字符串或自定义类型。切片是数组的一个引用,因此切片是引用类型,在进行传递时,遵守引用传递的机制,下面我们就来详细了解一下该内容
    2021-10-10

最新评论