Golang与C互用以及调用C的so动态库和a静态库
Golang与C的关系非常密切,下面主要介绍在Golang中使用C。
Golang中嵌入C代码
1 package main
2 //#include <stdio.h>
3 //#include <stdlib.h>
4 /*
5 void Hello(char *str) {
6 printf("%s\n", str);
7 }
8 */
9 import "C" //假设把C当成包,其实有点类似C++的名字空间
10 import "unsafe" //C指针的使用,在C代码中申请的空间,GC垃圾回收机制不会管理,所以需要自己手动free申请的空间
11 func main() {
12 s := "Hello Cgo"
13 cs := C.CString(s)
14 C.Hello(cs)
15 C.free(unsafe.Pointer(cs))
16 }
3,4行的注释也可以写/* */形式 第4行与第5行之间不能有空行,同样第9行与第10行之间也不 能有行,否则编译时cgo会报错:
Jermine@Jermine-VirtualBox:~/mygo/src/myC/tmp$ go run goc.go
# command-line-arguments
could not determine kind of name for C.Hello
could not determine kind of name for C.free
Golang中调用C的动态库so
C库源程序代码:
1 //foo.c
2 #include <stdio.h>
3
4 int Num = 8;
5
6 void foo(){
7 printf("I dont have new line.");
8 printf("I have new line\n");
9 printf("I have return\r");
10 printf("I have return and new line\r\n");
11 }
1 //foo.h
2 #ifndef __FOO_H
3 #define __FOO_H
4
5 #ifdef __cplusplus
6 extern "C" {
7 #endif
8
9 extern int Num;
10 extern void foo();
11
12 #ifdef __cplusplus
13 }
14 #endif
15
16 #endif
Go调用C库的代码:
1 package main
2
3 /*
4 #cgo CFLAGS: -I.
5 #cgo LDFLAGS: -L. -lfoo
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include "foo.h"
9 */
10 import "C"
11 import "unsafe"
12 import "fmt"
13 func Prin(s string) {
14 cs := C.CString(s)
15 defer C.free(unsafe.Pointer(cs))
16 C.fputs(cs, (*C.FILE)(C.stdout))
17 //C.free(unsafe.Pointer(cs))
18 C.fflush((*C.FILE)(C.stdout))
19 }
20
21 func main() {
22 fmt.Println("vim-go")
23 fmt.Printf("rannum:%x\n", C.random())
24 Prin("Hello CC")
25 fmt.Println(C.Num)
26 C.foo()
27 }
注意import “C”这行与第9行之间也不能有空格,原因同上编译会报错。 go与库、头文件的目录结构,以及编译:
Jermine@Jermine-VirtualBox:~/mygo/src/myC/sotest$go build cc.go //编译之后产生cc
Jermine@Jermine-VirtualBox:~/mygo/src/myC/sotest$ ls
cc cc.go foo.h libfoo.so
Jermine@Jermine-VirtualBox:~/mygo/src/myC/sotest$ export LD_LIBRARY_PATH=./
Jermine@Jermine-VirtualBox:~/mygo/src/myC/sotest$ ./cc
vim-go
rannum:6b8b4567
Hello CC8
I dont have new line.I have new line
I have return and new line
如果c库的程序改为下面这样:
1 //foo.c
2 #include <stdio.h>
3
4 int Num = 8;
5
6 void foo(){
7 printf("I dont have new line.");
8 //printf("I have new line\n");
9 //printf("I have return\r");
10 //printf("I have return and new line\r\n");
11 }
此时,go中调用foo时没有任何反应,即不会输出,不会输出的原因是printf后面没有加换行符,但是如果加了8,9,10这些测试行后,第7行也会显示,原因是第10行最后有一个换行符,这个应该是向stdout输出时,字符流是放在buffer中,如果没有换行,buffer中的数据不会立即输出。在go调用C的测试程序中有写了一个测试小函数用来测试stdout,验证了没有fflush,stdout上不会显示输出信息。但平时在写C程序的时候,似乎没有这样的问题,这个是因为C的运行环境自动做了fflush的动作,这个可能是Golang的不足,也许golan表g认为这样做更好。这只是个人的分析,如有分析不对的地方,请跟贴,谢谢。
Golang调用C的静态库a
所有源程序与动库演示代码类似,只是编译C库时,是用的静态编译,如下所示:
1Jermine@Jermine-VirtualBox:~/mygo/src/myC$ ls
2foo.c
3Jermine@Jermine-VirtualBox:~/mygo/src/myC$ gcc -c foo.c
4foo.c foo.o
5Jermine@Jermine-VirtualBox:~/mygo/src/myC$ ar -rv libfoo.a foo.o
6foo.c foo.o libfoo.a
Go调用的代码与动态库一样,下面是目录结构:
1Jermine@Jermine-VirtualBox:~/mygo/src/myC/atest$ ls
2cc cc.go foo.h libfoo.a
go调用C/C++使用值传递和指针传递
在实际项目中用到的不仅仅是基本类型之间的转换,更多的是函数封装中的值传递和指针传递,如何在C功能函数中和Go中进行各种值和指针传递呢?根本方法还是利用基本类型,包括特别常用unsafe.Pointer
1
2package main
3
4/*
5#include <stdio.h>
6#include <stdlib.h>
7#include <unistd.h>
8#include <string.h>
9
10#define MAX_FACES_PER_DETECT 64
11
12typedef struct Point{
13 float x;
14 float y;
15}Point;
16
17typedef struct Rectangle{
18 Point lt;
19 Point rd;
20}Rectangle;
21
22typedef struct DetectFaceInfo{
23 int id;
24 float score;
25 Rectangle pos;
26}DetectFaceInfo;
27
28
29
30void setStruct(void **ppDetectInfo)
31{
32 DetectFaceInfo *pDetectInfo = (DetectFaceInfo *)malloc(sizeof(DetectFaceInfo));
33 memset(pDetectInfo, 0 , sizeof(pDetectInfo));
34 pDetectInfo->id = 1;
35 pDetectInfo->score = 0.98f;
36 pDetectInfo->pos.lt.x = 1;
37 pDetectInfo->pos.lt.y = 1;
38 pDetectInfo->pos.rd.x = 9;
39 pDetectInfo->pos.rd.y = 10;
40
41 fprintf(stdout, "A pDetectInfo address : %p\n", pDetectInfo);
42 *ppDetectInfo = pDetectInfo;
43}
44
45int printStruct(void *pdetectinfo)
46{
47 DetectFaceInfo * pDetectInfo = (DetectFaceInfo *)pdetectinfo;
48 fprintf(stdout, "B pDetectInfo address : %p\n", pDetectInfo);
49
50 fprintf(stdout, "id: %d\n", pDetectInfo->id);
51 fprintf(stdout, "score : %.3lf\n", pDetectInfo->score);
52 fprintf(stdout, "pos.lt.x : %d\n", pDetectInfo->pos.lt.x);
53 fprintf(stdout, "pos.lt.y : %d\n", pDetectInfo->pos.lt.y);
54 fprintf(stdout, "pos.rd.x : %d\n", pDetectInfo->pos.rd.x);
55 fprintf(stdout, "pos.rd.y : %d\n", pDetectInfo->pos.rd.y);
56}
57
58int freeStruct(void *pDetectInfo)
59{
60 fprintf(stdout, "C pDetectInfo address : %p\n", pDetectInfo);
61 free((DetectFaceInfo*)pDetectInfo);
62}
63
64*/
65import "C"
66
67import (
68 _ "fmt"
69 _ "reflect"
70 "unsafe"
71)
72
73func main() {
74 var pDetectInfo unsafe.Pointer
75
76 C.setStruct(&pDetectInfo)
77 C.printStruct(pDetectInfo)
78 C.freeStruct(pDetectInfo)
79}
从上面的例子可以知道还是利用了C和go的基本类型转换,更多的是利用指针。得到的运行结果是:
A pDetectInfo address : 012832B0
B pDetectInfo address : 012832B0
id: 1
score : 0.980
pos.lt.x : 1.000
pos.lt.y : 1.000
pos.rd.x : 9.000
pos.rd.y : 10.000
C pDetectInfo address : 012832B0
这是通过C的结构体将数据传送给go,同样的也可以利用在go中处理数据,然后传递给C,也是利用基本类型转换。这里做个记录:
- C中的结构体如果带了typedef的话,那么在go中定义C结构体就不需要带struct了。反则反之。
- Go中定义C结构体: var struct C.DetectFaceInfo
- 再次注意,如果在结构体中有字符数组或者是char指针,注意区别
C/C++调用go实现struct值传递
1
2package main
3/*
4struct Vertex {
5 int X;
6 int Y;
7};
8*/
9import "C"
10import "fmt"
11
12//export getVertex
13func getVertex(X, Y C.int) C.struct_Vertex {
14 return C.struct_Vertex{X, Y}
15}
16
17func main() {
18 fmt.Println(getVertex(1, 2))
19}
execute go build -buildmode=c-shared -o go-test1.so go-test1.go 将得到.h文件和.so文件如下:
~/Desktop/test ⌚ 14:40:44
$ go build -buildmode=c-shared -o go-test1.so go-test1.go
~/Desktop/test ⌚ 14:41:05
$ ls
go-test1.go go-test1.h go-test1.so
~/Desktop/test ⌚ 14:41:06
$ cat go-test1.h
/* Code generated by cmd/cgo; DO NOT EDIT. */
/* package command-line-arguments */
#line 1 "cgo-builtin-prolog"
#include <stddef.h> /* for ptrdiff_t below */
#ifndef GO_CGO_EXPORT_PROLOGUE_H
#define GO_CGO_EXPORT_PROLOGUE_H
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
#endif
/* Start of preamble from import "C" comments. */
#line 2 "go-test1.go"
struct Vertex {
int X;
int Y;
};
#line 1 "cgo-generated-wrapper"
/* End of preamble from import "C" comments. */
/* Start of boilerplate cgo prologue. */
#line 1 "cgo-gcc-export-header-prolog"
#ifndef GO_CGO_PROLOGUE_H
#define GO_CGO_PROLOGUE_H
typedef signed char GoInt8;
typedef unsigned char GoUint8;
typedef short GoInt16;
typedef unsigned short GoUint16;
typedef int GoInt32;
typedef unsigned int GoUint32;
typedef long long GoInt64;
typedef unsigned long long GoUint64;
typedef GoInt64 GoInt;
typedef GoUint64 GoUint;
typedef __SIZE_TYPE__ GoUintptr;
typedef float GoFloat32;
typedef double GoFloat64;
typedef float _Complex GoComplex64;
typedef double _Complex GoComplex128;
/*
static assertion to make sure the file is being used on architecture
at least with matching size of GoInt.
*/
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];
typedef _GoString_ GoString;
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
#endif
/* End of boilerplate cgo prologue. */
#ifdef __cplusplus
extern "C" {
#endif
extern struct Vertex getVertex(int p0, int p1);
#ifdef __cplusplus
}
#endif
~/Desktop/test ⌚ 14:41:22
$ file go-test1.so
go-test1.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=59440090a28e0869a12951bb9fc4b311e038ee8f, not stripped
Golang调用C++函数
lib.cpp file (这里面你可以写 c++ 代码 ,但是go 需要调用的,你需要写成 红色标记的方式导出)
1extern "C" int start(void); //标记的方式导出
2
3int start()//int argc, char **argv
4{
5 return;
6}
注意:go不能直接调用C++,必须要以C的函数导出才能供go调用。
lib.h
int start(void);
1package main
2
3/*
4#include "lib.h"
5#cgo LDFLAGS: -L.. -llib
6
7*/
8import "C"
9
10func main() {
11 C.start()
12}
13gcc -g -fPIC -c -o lib.o lib.cpp
14gcc -g -fPIC -shared -o liblib.so lib.o
其他参照:
https://blog.csdn.net/zdy0_2004/article/details/79124269
