文章
问答
冒泡
通过cgo调用静态/动态链接库

前言

上一篇文章简单介绍了go语言调用c语言,但是在调用动态链接库的时候是通过syscall的三种函数去调用,本文主要介绍go语言通过cgo调用静态/动态链接库。

上一篇文章:Go语言简单调用C语言

https://www.ithere.net/article/1682222608420450305

通过go生成链接库

package main

import "C"

//export Add
func Add(a C.int, b C.int) C.int {
	return a + b
}

//export Print
func Print(s *C.char) {
	print("Hello ", C.GoString(s))
}

// 通过指令导出动态链接库 go build -buildmode=c-shared -o lib/demoDll.dll demoDll.go
// 通过指令导出静态链接库 go build -buildmode=c-archive -o lib/demoDll2.lib demoDll.go
//
// 编译单个文件需要main函数,运行是要注释掉,一个go程序只能有一个main入口
func main() {}

通过指令go build -buildmode=c-shared -o lib/demoDll.dll demoDll.go在lib目录下生成动态链接库,demoDll.h和demoDll.dll两个文件

通过指令go build -buildmode=c-archive -o lib/demoDll2.lib demoDll.go在lib目录下生成静态链接库demoDll2.h和demoDll2.lib两个文件

cgo调用

调用动态链接库

package main

//#cgo CFLAGS: -I./lib
//#cgo windows LDFLAGS: -L${SRCDIR}/lib -ldemoDll
//
//#include "demoDll.h"
//#include <stdlib.h>
import "C"
import (
	"fmt"
	"unsafe"
)

func demo1() {
	fmt.Println(C.Add(10, 5))
	cs := C.CString("World1")
	C.Print(cs)
	defer C.free(unsafe.Pointer(cs))
	fmt.Println("")
}

调用静态链接库

package main

//#cgo CFLAGS: -I./lib
//#cgo windows LDFLAGS: -L${SRCDIR}/lib -ldemoDll2
//
//#include "demoDll2.h"
//#include <stdlib.h>
import "C"
import (
	"fmt"
	"unsafe"
)

func demo2() {
	fmt.Println(C.Add(4, 5))
	cs := C.CString("World2")
	C.Print(cs)
	defer C.free(unsafe.Pointer(cs))
}

mian方法

package main

func main() {
	demo1()
	demo2()
}

最后输出

15

Hello World1

9

Hello World2

#cgo参数

上面能调用成功主要是通过#cgo指令符让编译器可以找到对应的文件

编译参数:CFLAGS/CPPFLAGS/CXXFLAGS

编译参数主要是头文件的检索路径,预定义的宏等参数。理论上来说C和C++是完全独立的两个编程语言,它们可以有着自己独立的编译参数。 但是因为C++语言对C语言做了深度兼容,甚至可以将C++理解为C语言的超集,因此C和C++语言之间又会共享很多编译参数。 因此CGO提供了CFLAGS/CPPFLAGS/CXXFLAGS三种参数,其中CFLAGS对应C语言编译参数(以.c后缀名)、 CPPFLAGS对应C/C++ 代码编译参数(.c,.cc,.cpp,.cxx)、CXXFLAGS对应纯C++编译参数(.cc,.cpp,*.cxx)。

链接参数:LDFLAGS

链接参数主要包含要链接库的检索目录和要链接库的名字。因为历史遗留问题,链接库不支持相对路径,我们必须为链接库指定绝对路径。 cgo 中的 ${SRCDIR} 为当前目录的绝对路径。经过编译后的C和C++目标文件格式是一样的,因此LDFLAGS对应C/C++共同的链接参数。

条件选择

#cgo指令还支持条件选择,当满足某个操作系统或某个CPU架构类型时后面的编译或链接选项生效。比如下面是分别针对windows和非windows下平台的编译和链接选项

// #cgo windows CFLAGS: -DX86=1
// #cgo !windows LDFLAGS: -lm
// #cgo amd64 CFLAGS: -lpthread
// #cgo arm64 CFLAGS: -lpng

如上就是在不同操作系统预定义宏,链接不同库的操作。

如果在不同的系统下cgo对应着不同的c代码,我们可以先使用#cgo指令定义不同的C语言的宏,然后通过宏来区分不同的代码:

package main

/*
#cgo windows CFLAGS: -DCGO_OS_WINDOWS=1
#cgo darwin CFLAGS: -DCGO_OS_DARWIN=1
#cgo linux CFLAGS: -DCGO_OS_LINUX=1

#if defined(CGO_OS_WINDOWS)
    const char* os = "windows";
#elif defined(CGO_OS_DARWIN)
    const char* os = "darwin";
#elif defined(CGO_OS_LINUX)
    const char* os = "linux";
#else
#    error(unknown os)
#endif
*/
import "C"

func main() {
    print(C.GoString(C.os))
}

参考文章

https://books.studygolang.com/advanced-go-programming-book/ch2-cgo/ch2-10-link.html

https://blog.csdn.net/Canger_/article/details/121039793

golang

关于作者

TimothyC
天不造人上之人,亦不造人下之人
获得点赞
文章被阅读