C可以调用Go,并且Go可以调用C, 如果更进一步呢, C-->Go-->C
或者 Go-->C-->Go
的调用如何实现?
本文通过两个简单的例子帮助你了解这两种复杂的调用关系。本文不涉及两者之间的复杂的数据转换,官方文章C? Go? Cgo!、wiki/cgo和cmd/cgo有一些介绍。
Go-->C-->Go
Go程序调用C实现的函数,然后C实现的函数又调用Go实现的函数。
1、首先,我们新建一个hello.go
的文件:
|
|
它定义了一个HelloFromGo
函数,注意这个函数是一个纯的Go函数,我们定义它的输出符号为HelloFromGo
。
2、接着我们新建一个hello.c
的文件:
|
|
这个c文件定义了一个C函数helloFromC
,内部它会调用我们刚才定义的HelloFromGo
函数。
这样,我们实现了C
调用Go
: C-->Go
,下面我们再实现Go调用C。
3、最后新建一个main.go
文件:
|
|
它调用第二步实现的C函数helloFromC
。
运行测试一下:
|
|
可以看到,期望的函数调用正常的运行。第一行是C函数的输出,第二行是Go函数的输出。
C-->Go-->C
第二个例子演示了C程序调用Go实现的函数,然后Go实现的函数又调用C实现的函数。
1、首先新建一个hello.c
文件:
|
|
它定义了一个纯C实现的函数。
2、接着新建一个hello.go
文件:
|
|
它实现了一个Go函数HelloFromGo
,内部实现调用了C实现的函数helloFromC
,这样我们就实现了Go-->C
。
注意包名设置为package main
,并且增加一个空的main
函数。
运行go build -o hello.so -buildmode=c-shared .
生成一个C可以调用的库,这调命令执行完后会生成hello.so
文件和hello.h
文件。
3、最后新建一个文件夹,随便起个名字,比如main
将刚才生成的hello.so
文件和hello.h
文件复制到main
文件夹,并在main
文件夹中新建一个文件main.c
:
|
|
运行gcc -o main main.c hello.so
生成可执行文件main
, 运行main
:
|
|
第一行输出来自main.c
,第二行来自Go函数,第三行来自hello.c
中的C函数,这样我们就实现了C-->Go--C
的复杂调用。
C-->Go-->C
的状态变量
我们来分析第二步中的一个特殊的场景, 为了下面我们好区分,我们给程序标记一下, 记为C1-->Go-->C2
, C2的程序修改一下,加入一个状态变量a
,并且函数helloFromC
中会打印a
的地址和值,也会将a
加一。
|
|
然后修改main.c
程序,让它既通过Go嗲用C1.helloFromC
,又直接调用C1.helloFromC
,看看多次调用的时候a
的指针是否一致,并且a
的值是否有变化。
|
|
激动人心的时候到了。我们不同的编译方式会产生不同的结果。
1、gcc -o main main.c hello.so
和第二步相同的编译方式,编译出main
并执行, 因为hello.so
中包含C1.helloFromC
实现,所以可以正常执行。
|
|
可以看到a
的指针是同一个值,无论通过Go函数改变还是通过C函数改变都是更改的同一个变量。
nm可以查看生成的main
的符号:
|
|
U
代表这个符号是未定义的符号,通过动态库链接进来。
2、 gcc -o main main.c hello.so ../hello.c
我们编译的时候直接链接hello.c
的实现,然后运行main
:
|
|
可以看到a
是不同的两个变量。
nm可以查看生成的main
的符号:
|
|
可以看到_a
是初始化的环境变量,_helloFromC
的类型是T
而不是U
,代表它是一个全局的Text符号,这和上一步是不一样的。
参考文档
- https://medium.com/using-go-in-mobile-apps/using-go-in-mobile-apps-part-1-calling-go-functions-from-c-be1ecf7dfbc6
- https://github.com/vladimirvivien/go-cshared-examples
- http://golang.org/cmd/cgo
- https://gist.github.com/zchee/b9c99695463d8902cd33
- https://medium.com/@liamkelly17/working-with-packed-c-structs-in-cgo-224a0a3b708b
- https://groups.google.com/forum/#!topic/golang-nuts/EhndTzcPJxQ
- https://docs.google.com/document/d/1nr-TQHw_er6GOQRsF6T43GGhFDelrAP0NqSS_00RgZQ/edit#
- https://www.mkssoftware.com/docs/man1/nm.1.asp