Master CGO: Seamlessly Integrate C Libraries into Go Projects
This comprehensive guide explains CGO fundamentals, demonstrates basic and advanced usage with practical code examples, covers type conversion, calling C standard libraries, handling external C files, callbacks, struct passing, memory‑management best practices, real‑world scenarios, limitations, and alternative approaches for Go developers.
CGO Basics
CGO is a tool provided by the Go language that enables Go programs to call C code and vice versa. It works by embedding special comments and directives within Go source files.
What is CGO
CGO allows Go programs to invoke C functions and C code to call Go functions.
Simple CGO Example
package main
/*
#include <stdio.h>
#include <stdlib.h>
void myprint(char* s) {
printf("%s
", s);
}
*/
import "C"
import "unsafe"
func main() {
cs := C.CString("Hello from stdio")
C.myprint(cs)
C.free(unsafe.Pointer(cs)) // must manually free C‑allocated memory
}In this example:
Write C code inside a /* */ comment block.
Use import "C" to enable CGO.
Call C functions with the C. prefix.
Convert Go strings to C strings with C.CString.
Manually free memory allocated by C.
Core CGO Features
Type Conversion
Explicit conversion is required between Go and C types. Common mappings include:
Go string ↔ C char* via C.CString Go []byte ↔ C void* via C.CBytes Go int ↔ C int via C.int Go int64 ↔ C long long via C.longlong Go float64 ↔ C double via
C.double // Go string to C string
cstr := C.CString("gopher")
defer C.free(unsafe.Pointer(cstr))
// Go byte slice to C void*
data := []byte{1, 2, 3}
cbytes := C.CBytes(data)
defer C.free(unsafe.Pointer(cbytes))Calling the C Standard Library
You can directly invoke functions from the C standard library:
package main
/*
#include <math.h>
*/
import "C"
import "fmt"
func main() {
fmt.Println(C.sqrt(2)) // call sqrt from math.h
}Using External C Files
For more complex C code, place it in separate files and include the headers:
// main.go
package main
/*
#include "myclib.h"
*/
import "C"
func main() {
C.my_c_function()
} // myclib.h
void my_c_function(); // myclib.c
#include "myclib.h"
#include <stdio.h>
void my_c_function() {
printf("Hello from C!
");
}Compile both files together, e.g., go build -o main.
Advanced CGO Techniques
Callbacks: C Calls Go
C code can call Go functions, but the Go functions must be exported and reside in the main package without return values:
package main
/*
extern void goCallback(int);
static void callGoFunc() {
goCallback(42);
}
*/
import "C"
import "fmt"
//export goCallback
func goCallback(value C.int) {
fmt.Printf("Callback from C with value: %d
", int(value))
}
func main() {
C.callGoFunc()
}Struct Passing
Structures can be shared between Go and C:
package main
/*
typedef struct {
int x;
int y;
} Point;
void printPoint(Point p) {
printf("Point: (%d, %d)
", p.x, p.y);
}
*/
import "C"
import "unsafe"
func main() {
point := C.Point{x: 10, y: 20}
C.printPoint(point)
}Memory‑Management Best Practices
Key rules for safe memory handling in CGO:
C‑allocated memory must be released with C.free (or appropriate C deallocation functions).
Go‑allocated memory is managed by the garbage collector, but when passed to C it must be kept alive until C finishes using it.
Use defer to ensure temporary C allocations are freed promptly.
cs := C.CString("temporary string")
defer C.free(unsafe.Pointer(cs))Practical Use Cases
Calling High‑Performance C Math Libraries
package main
/*
#cgo LDFLAGS: -lm
#include <math.h>
*/
import "C"
import "fmt"
func main() {
fmt.Println("Sin(1) =", C.sin(1))
fmt.Println("Log(10) =", C.log(10))
}The math library is linked automatically during compilation.
Using Third‑Party C Libraries (e.g., SQLite)
package main
/*
#cgo LDFLAGS: -lsqlite3
#include <sqlite3.h>
*/
import "C"
import "fmt"
func main() {
var db *C.sqlite3
if C.sqlite3_open(C.CString(":memory:"), &db) != C.SQLITE_OK {
fmt.Println("Failed to open database")
return
}
defer C.sqlite3_close(db)
// Use SQLite via C API...
}Limitations and Alternatives
CGO Limitations
Requires a system C compiler.
Cross‑compilation is more complex.
Calls between Go and C incur performance overhead.
Memory management can be error‑prone, leading to leaks.
Alternative Approaches
Pure Go implementations that provide similar functionality.
Direct system calls using the syscall package.
SWIG for generating bindings to more complex C/C++ code.
Compile C code as a shared library and load it with Go's plugin package.
Conclusion
CGO gives Go developers powerful interoperability with C, allowing reuse of existing C libraries, performance‑critical optimizations, and access to low‑level system features. However, it adds complexity, so it should be used judiciously. When using CGO, follow best practices such as minimizing call boundaries, strictly managing memory lifetimes, documenting interface contracts, and thoroughly testing the C side.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
php Courses
php中文网's platform for the latest courses and technical articles, helping PHP learners advance quickly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
