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

https://studygolang.com/articles/16296

https://blog.csdn.net/a1368783069/article/details/84142924