C can call go, and go can call C, if it went further, C-->Go-->C
or Go-->C-->Go
How is the call implemented?
This article uses two simple examples to help you understand these two complex invocation relationships. This article does not deal with the complex data conversion between the two, the official article C? Go? Cgo!, Wiki/cgo and Cmd/cgo have some introductions.
Go-->c-->go
The Go program calls the C-implemented function, and then the C-implemented function calls the GO implementation function.
1. First, we create a new hello.go
file:
hello.go
123456789 |
Package mainimport"C"import"FMT"//export Hellofromgo func Hellofromgo () {fmt. Printf ("Hello from go!\n")} |
It defines a HelloFromGo
function, note that the function is a pure go function, and we define its output symbol as HelloFromGo
.
2. Then we create a new hello.c
file:
hello.c
123456789 |
#include <stdio.h>#include "_cgo_export.h"int HELLOFROMC () { printf("Hi from c\n"); //call Go function Hellofromgo (); return 0;} |
This c file defines a C function helloFromC
, inside it calls the function we just defined HelloFromGo
.
In this way, we implement the C
call Go
: C-->Go
, below we implement go call C.
3. Finally create a new main.go
file:
main.go
1234567891011 |
Package Main/*extern int HELLOFROMC (); */import"C"func main () { //call C function C.HELLOFROMC ()} |
It invokes the C function implemented in the second step helloFromC
.
Run the test for a moment:
123 |
$ go run. Hi from Chello from go! |
As you can see, the expected function call runs normally. The first line is the output of the C function, and the second line is the output of the GO function.
C-->go-->c
The second example shows the functions that the C program calls the go implementation, and then the go implementation function calls the C implementation function.
1. Create a new hello.c
file first:
hello.c
123456 |
#include <stdio.h>int Hellofromc () { printf("Hi from c\n"); return 0;} |
It defines a pure C implementation of the function.
2. Then create a new hello.go
file:
12345678910111213141516171819 |
//Go build-o hello.so-buildmode=c-shared. Package Main/*extern int HELLOFROMC (); */import"C"import"FMT" //export Hellofromgofunc Hellofromgo () {fmt. Printf ("Hello from go!\n") C.HELLOFROMC ()}func main () {} |
It implements a Go function HelloFromGo
, the internal implementation calls the C implementation of the function helloFromC
, so that we have implemented Go-->C
.
Note The package name is set to package main
, and an empty main
function is added.
Run go build -o hello.so -buildmode=c-shared .
generates a library that can be called by C, which generates hello.so
files and files when the command finishes hello.h
.
3, finally create a new folder, a name, such asmain
Copy the files and files that you just generated hello.so
to the folder and hello.h
main
main
Create a new file in the folder main.c
:
main.c
12345678910 |
#include <stdio.h>#include "hello.h"int main () { printf ("Use Hello lib from c:\n"); Hellofromgo (); return 0;} |
Run gcc -o main main.c hello.so
the build executable file main
and run main
:
1234 |
$./mainuse Hello Lib from C:hello from go! Hi from C |
The first line of output comes from the main.c
second line from the Go function, and the third line comes from hello.c
the C function in, so that we implement C-->Go--C
the complex call.
C-->Go-->C
The state variable
Let us analyze a special scene in the second step, for the following we distinguish, we give the program mark, remember C1-->Go-->C2
, C2 program modified, add a state variable a
, and the function helloFromC
will print a
the address and value, also will add a
one.
hello.c
12345678 |
#include <stdio.h>int 1; int Hellofromc () { printf("Hi from C:%p,%d\n", &a, a++); return 0;} |
Then modify the main.c
program so that it is used both by go and C1.helloFromC
directly, to see if the pointer is consistent at C1.helloFromC
multiple calls, a
and a
whether the value changes.
Main.c
1234567891011121314151617 |
#include <stdio.h>#include "hello.h"int main () { printf ("Use Hello lib from c:\n"); //1. Call C function directly HELLOFROMC (); //2. Call the Go function Hellofromgo (); //3. Call C function directly HELLOFROMC (); return 0;} |
It's a thrilling time. Our different ways of compiling produce different results.
1.gcc -o main main.c hello.so
And the second step of the same compilation, compiled main
and executed, because hello.so
the C1.helloFromC
implementation is included, so it can be executed normally.
You can see that a
the pointer is the same value, whether through the go function change or through the C function changes are the same variable.
NM to view the generated main
symbols:
1234567 |
NM main u _hellofromgo0000000100000000 T __mh_execute_header u _hellofromc0000000100000 F10 T _main u _printf u dyld_stub_binder |
U
Represents the symbol that is undefined and is linked through a dynamic library.
2.gcc -o main main.c hello.so ../hello.c
We compile the implementation of the direct link hello.c
, and then run main
:
You can see a
that the two variables are different.
NM to view the generated main
symbols:
12345678 |
NM main U _hellofromgo0000000100000000 T __mh_execute_header0000000100001020 D _a 0000000100000F10 T _HELLOFROMC0000000100000ec0 T _main u _printf u dyld_stub_binder |
You can see that the _a
environment variable is initialized, and _helloFromC
the type is T
instead U
, which represents a global text symbol, which is not the same as the previous step.
Reference documents
- 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