前言
上一篇文章简单介绍了go语言调用c语言,但是在调用动态链接库的时候是通过syscall的三种函数去调用,本文主要介绍go语言通过cgo调用静态/动态链接库。
上一篇文章:Go语言简单调用C语言
通过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