CGO: Go's Bridge to C
You need to import “C” for CGO.
The binary bridge interface works this way: Go’s memory scales while C’s memory stays stable. Go can access static memory easily, but C accessing Go’s memory causes problems. To handle passing Go memory to C simply and efficiently, CGO defines special rules. During the C function call, CGO guarantees the Go memory won’t move. The C function can use Go memory safely.
// #include <stdio.h>
import "C"This line enables CGO and imports C’s standard IO library.
C.CString("hello, world") // converts Go string to C const char *Call custom functions with C.function_name(args). You can write the implementation directly in comments, or put it in .c files first then declare it in comments in .go files. The more formal way puts declarations in .h files and includes them in comments.
C types in Go:
// char
type C.char
type C.schar (signed char)
type C.uchar (unsigned char)
// short
type C.short
type C.ushort (unsigned short)
// int
type C.int
type C.uint (unsigned int)
// long
type C.long
type C.ulong (unsigned long)
// longlong
type C.longlong (long long)
type C.ulonglong (unsigned long long)
// float
type C.float
// double
type C.double
// struct XXX
type C.struct_XXX
// var a struct.XXX
// a.field Add _ prefix if field name conflicts with keywords
// union XXX
// Similar to struct access, use corresponding byte arrays in GoWhen passing arguments, wrapper functions convert Go types to C types. Types imported through the virtual C package ignore Go’s capitalization rules. Build and link parameters define through comments like:
// #cgo [FLAGS] [VAL]Handle different platforms:
/*
#cgo windows CFLAGS: -DCGO_OS_WINDOWS=1
#cgo darwin CFLAGS: -DCGO_OS_DARWIN=1
#cgo linux CFLAGS: -DCGO_OS_LINUX=1
#if defined(CGO_OS_WINDOWS)
#elif defined(CGO_OS_DARWIN)
#elif defined(CGO_OS_LINUX)
#else
#endif
*/Build tag conditional compilation:
// +build tag1The file compiles only when go build -tags=".." includes this tag.
Go Calls C
To convert Go variables to C types, wrapper functions copy and convert internally. For strings, they convert the underlying byte slice to C byte arrays. This implicitly calls C’s malloc for dynamic memory allocation. You must eventually free it. So memory allocation and data copying are CGO’s main costs.
Solve pointer conversion: Use unsafe.Pointer to remove type information, then convert to other pointer types.
Solve number to pointer: Convert to uintptr, then wrap with unsafe.Pointer, then convert to others.
Function calls work differently because C doesn’t support multiple return values. Error handling uses the second return value as C’s global error variable errno. This maps to syscall.Errno type.
For void functions, use the above approach to get errors. Void returns use [0]byte empty arrays in Go.
Internal Mechanism
CGO creates two intermediate files for each Go file containing CGO code: one .go file and one .c file. It creates a type-related .go intermediate file for the entire package. A set of .c and .h files corresponds to Go types and functions exported to C.
The Go runtime eventually calls runtime.cgocall to invoke C functions and gets return values when done.
Memory Model
If C needs to access Go memory through pointers, the passed memory may be unsafe because Go’s memory is dynamic.
For example, C.CString() copies Go string data to new C memory space.
To avoid this inefficient problem, CGO guarantees that during the C function call, the passed Go memory won’t move. But you must pass it directly rather than saving it in temporary variables and passing indirectly.
Export Non-Main Package Functions
By default, C functions must come from main packages. If a separate subpackage needs to export functions, write the C function header files yourself.
Add comment //export function_name to the implementation function, then generate .a static libraries.
$ go build -buildmode=c-archive -o main.aWhen compiling C programs, include the static library directory. Just redeclare in your program to use it.