foreword
One of the advantages of the Go language is that it can call C code, write C code directly in the Go source code, or import an external library of C language. In this way, it can be rewritten where performance encounters bottlenecks, or some functions are missing from Go and third parties, but C language has ready-made libraries that can be used directly.
Below are several ways to demonstrate how Go calls C, and introduce the cross-compilation method ported to the arm platform (the cross-compilation method for other platforms is similar).
Embedded C code
The following code directly embeds the program written in C in the Go program. In Go, a "C" package needs to be introduced immediately after the C code. Define an add function and call it in main.
// main.go
package main
/*
int add(int a, int b)
{
return a + b;
}
*/
import "C"
import "fmt"
func main() {
a := C.int(10)
b := C.int(20)
val := C.add(a,b)
fmt.Printf("a+b=%v\n", val)
}
A single file can be run directly using go run main.go.
Cross-compilation:
luxq@luxq-vmpc:go_call_c$CGO_ENABLED=1 GOOS=linux GOARCH=arm CC=arm-linux-gnueabihf-gcc go build -o main
luxq@luxq-vmpc:go_call_c$file main
main: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 2.6.31, BuildID[sha1]=e7912b8c5b526f52c64a9b33c3e9df751ee9b903, not stripped
Standalone C source code file
The content of each file is as follows:
// add.h
#ifndef ADD_H
#define ADD_H
int add(int a, int b);
#endif /*ADD_H*/
The internal add function defined in add.c
// add.c
#include "add.h"
int add(int a, int b )
{
return a+b;
}
Define the external use interface in foo.h
// foo.h
#ifndef FOO_H
#define FOO_H
#include <stdio.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
extern int Num;
extern void foo();
extern int f_add(int a, int b);
#ifdef __cplusplus
}
#endif
#endif /*FOO_H*/
The interface is implemented in foo.c, and the add function defined in add.h is called at the same time.
// foo.c
#include <stdio.h>
#include "add.h"
int Num = 8;
void foo()
{
printf("First line.\n");
}
int f_add(int a, int b)
{
return add(a,b);
}
1. Same level directory
This situation means that C code and Go code are mixed in the same directory, and the function that calls C code in Go.
The directory structure is as follows:
go_call_c/
├── add.c
├── add.h
├── foo.c
├── foo.h
└── main.go
When all source codes are in the same directory, only the header file foo.h that directly calls the interface needs to be introduced in main.go, as follows:
// main.go
package main
/*
#include "foo.h"
*/
import "C"
import "unsafe"
import "fmt"
func Prin(s string) {
cs := C.CString(s)
defer C.free(unsafe.Pointer(cs))
C.fputs(cs, (*C.FILE)(C.stdout))
C.fflush((*C.FILE)(C.stdout))
}
func main() {
fmt.Println("rannum:%x\n", C.random())
Prin("Hello CC")
fmt.Println(C.Num)
C.foo()
a:=C.int(1)
b:=C.int(2)
value:= C.f_add(a,b)
fmt.Println("a+b=%v\n", value);
}
Since there are multiple files, you need to use go build to compile and then execute.
luxq@luxq-vmpc:go_call_c$go build -o main
luxq@luxq-vmpc:go_call_c$./main
rannum:%x
1804289383
Hello CC8
First line.
a+b=3
luxq@luxq-vmpc:go_call_c$
Cross compile:
luxq@luxq-vmpc:go_call_c$CGO_ENABLED=1 GOOS=linux GOARCH=arm CC=arm-linux-gnueabihf-gcc go build -o main
luxq@luxq-vmpc:go_call_c$file main
main: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 2.6.31, BuildID[sha1]=3bd34af6f6e5543cb2867490302ab1ac307c4fae, not stripped
luxq@luxq-vmpc:go_call_c$
2. Different levels of directories
This situation means that the C code and the Go code are not in the same directory, and the function that calls the C code in Go.
The directory structure is as follows:
go_call_c/
├── c_src
│ ├── add.c
│ ├── add.h
│ ├── foo.c
│ └── foo.h
└── main.go
In this case, main.go not only needs to import the header file foo.h that directly calls the interface, but also needs to import related C files, as follows:
// main.go
package main
/*
#include "c_src/foo.h"
#include "c_src/foo.c"
#include "c_src/add.c"
*/
import "C"
import "unsafe"
import "fmt"
func Prin(s string) {
cs := C.CString(s)
defer C.free(unsafe.Pointer(cs))
C.fputs(cs, (*C.FILE)(C.stdout))
C.fflush((*C.FILE)(C.stdout))
}
func main() {
fmt.Println("rannum:%x\n", C.random())
Prin("Hello CC")
fmt.Println(C.Num)
C.foo()
a:=C.int(1)
b:=C.int(2)
value:= C.f_add(a,b)
fmt.Println("a+b=%v\n", value);
}
It is also necessary to use go build to compile and then execute the generated executable file.
luxq@luxq-vmpc:go_call_c$go build -o main
luxq@luxq-vmpc:go_call_c$./main
rannum:%x
1804289383
Hello CC8
First line.
a+b=3
luxq@luxq-vmpc:go_call_c$
Cross compile:
luxq@luxq-vmpc:go_call_c$CGO_ENABLED=1 GOOS=linux GOARCH=arm CC=arm-linux-gnueabihf-gcc go build -o main
luxq@luxq-vmpc:go_call_c$file main
main: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 2.6.31, BuildID[sha1]=63ca836b1b931d7dc058039fd4c86a210c347640, not stripped
luxq@luxq-vmpc:go_call_c$
external library form
This method may be the most commonly used method, which is to first compile the C code into a library file, and then reference the library in Go. The
content of each C code file is the same as the above.
The directory structure is as follows:
go_call_c/
├── c_src
│ └── foo.c
│ └── add.h
│ └── add.c
├── include
│ └── foo.h
├── lib
│ └── libfoo.so
└── src
└── main.go
Compile the files under c_src into lib/libfoo.so (not described), and then reference the library in src/main.go.
// main.go
package main
/*
#cgo CFLAGS : -I../include/
#cgo LDFLAGS : -L../lib -lfoo
#include "foo.h"
*/
import "C"
import "unsafe"
import "fmt"
func Prin(s string) {
cs := C.CString(s)
defer C.free(unsafe.Pointer(cs))
C.fputs(cs, (*C.FILE)(C.stdout))
C.fflush((*C.FILE)(C.stdout))
}
func main() {
fmt.Println("rannum:%x\n", C.random())
Prin("Hello CC")
fmt.Println(C.Num)
C.foo()
}
Also compile main.go first and then execute it.
luxq@luxq-vmpc:src$go build -o main
luxq@luxq-vmpc:src$./main
rannum:%x
1804289383
Hello CC8
First line.
luxq@luxq-vmpc:src$
Cross-compilation:
When compiling, you need to ensure that the library file is already in the arm platform format.
luxq@luxq-vmpc:src$CGO_ENABLED=1 GOOS=linux GOARCH=arm CC=arm-linux-gnueabihf-gcc go build -o main
luxq@luxq-vmpc:src$file main
main: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 2.6.31, BuildID[sha1]=bae4b9df39d88a81dad2f07281ee17d1065a0549, not stripped
luxq@luxq-vmpc:src$
Summarize
Summarize yourself.