本文实践 go 语言引用 c 源文件,通过 go 语言封装的实例,在嵌入式开发过程中,
此方法可以解决很多实际问题。
自定义包的步骤
第一步
编写包的程序,本文以go 调用 c 语言为例,在 GOROOT/src 下建立 util包 文件夹,内容如下:
robot@ubuntu:/usr/local/go/src/util$ ls
go.mod util.c util.go util.h
robot@ubuntu:/usr/local/go/src/util$ cat util.h
int cSum(int x, int y);
robot@ubuntu:/usr/local/go/src/util$ cat util.c
#include "util.h"
int cSum(int x, int y){
return (x+y);
}
robot@ubuntu:/usr/local/go/src/util$ cat util.go
package util
/*
#include "util.h"
*/
import "C"
import "log"
func GoSum(x, y int) int {
z := int(C.cSum(C.int(x), C.int(y)))
log.Printf("goSum rs:%d\n",z)
return z
}
robot@ubuntu:/usr/local/go/src/util$ cat go.mod
module util
go 1.15
robot@ubuntu:/usr/local/go/src/util$ go build . //编译包
robot@ubuntu:/usr/local/go/src/util$ go install //安装包
包名称与文件夹名称最优是同名,在程序中import package 时,和调用包内容名称是一致的,利用包的管理。
第二步
调用自定义包,工作路径及内容如下:
robot@ubuntu:~/go-workspace/src/project/package$ ls
go.mod main.go pkg
robot@ubuntu:~/go-workspace/src/project/package$ cat main.go
package main
import "fmt"
import "util" //调用自定义包,导入的是包的文件夹名称
func main() {
res := util.GoSum(12,3)
fmt.Printf("This is main,res: %d \n",res)
}
robot@ubuntu:~/go-workspace/src/project/package$ cat go.mod // go mod init pkg 产生的 module pkg 内容
module pkg
go 1.15
robot@ubuntu:~/go-workspace/src/project/package$ go build . // 使用 module 管理源码时,可以使用 go build . 编译生成 pkg包名称可执行文件
robot@ubuntu:~/go-workspace/src/project/package$ ./pkg // 执行
2021/08/18 23:58:50 goSum rs:15
This is main,res: 15
robot@ubuntu:~/go-workspace/src/project/package$
我们可以总结以下几点:
(1)import语句使用的是文件夹的名称
(2)文件夹的名称和package的名称不一定相同,为了更换管理包我们一般采用文件夹与包名称同名
(3)调用自定义包使用package名称.函数名的方式
例如上面使用的util.GoSum(),用go把C 语言的调用封装,此方式可以把常用的 C语言代码封装成包。
(4)自定义包的调用和源码文件名没有关系
例如上面的util.go文件,如果改成util_c.go,程序也能正常编译。
实验过程中产生错误信息
~/go-workspace/src/project/cgo/c-util$ go build util.go
# command-line-arguments
./util.go:12:6: could not determine kind of name for C.Sum
// 切记!!! 在注释和import”C”之间不能有空行
:~/go-workspace/src/project/cgo/c-util$ go build util.go
# command-line-arguments
./util.go:12:2: cannot use x (type _Ctype_int) as type int in return argument
Go中 C原生的数值类型
数值类型 :
C.char,
C.schar (signed char),
C.uchar (unsigned char),
C.short,
C.ushort (unsigned short),
C.int, C.uint (unsigned int),
C.long,
C.ulong (unsigned long),
C.longlong (long long),
C.ulonglong (unsigned long long),
C.float,
C.double
指针类型
- 原生数值类型的指针类型可按Go语法在类型前面加上,比如var p *C.int,而void比较特殊,用Go中的unsafe.Pointer表示。
- 任何类型的指针值都可以转换为unsafe.Pointer类型,而unsafe.Pointer类型值也可以转换为任意类型的指针值。
- unsafe.Pointer还可以与uintptr这个类型做相互转换。
- 由于unsafe.Pointer的指针类型无法做算术操作,转换为uintptr后可进行算术操作。
字符串类型
- C语言中并不存在正规的字符串类型,在C中用带结尾’\0’的字符数组来表示字符串;
- 在Go中,string类型是原生类型,因此在两种语言互操作是势必要做字符串类型的转换。
通过C.CString函数,我们可以将Go的string类型转换为C的”字符串”类型,再传给C函数使用。
eg:
s := “Hello Cgo\n”
cs := C.CString(s)
C.print(cs)
数组类型
C语言中的数组与Go语言中的数组差异较大,后者是值类型,而C 数组与指针大部分场合都可以随意转换。
目前似乎无法直接显式的在两者之间进行转型,官方文档也没有说明。但我们可以通过编写转换函数,
将C的数组转换为Go的Slice(由于Go中数组是值类型,其大小是静态的,转换为Slice更为通用一些),
下面是数组转换的例子的链接。
对于 cgo 相关例子内容,可以参考官方文档。www.golang.org