Cgo and Foreign Function Interfaces
Overview
Cgo enables calling C code from Go. It’s useful for interfacing with system libraries but adds complexity.
Basic Cgo
package main
/*
#include <stdio.h>
void hello() {
printf("Hello from C!\n");
}
*/
import "C"
func main() {
C.hello()
}Passing Data
/*
int add(int a, int b) {
return a + b;
}
*/
import "C"
func main() {
result := C.add(C.int(2), C.int(3))
fmt.Println(int(result)) // 5
}Strings
/*
#include <stdlib.h>
#include <string.h>
char* greet(const char* name) {
char* buf = malloc(100);
sprintf(buf, "Hello, %s!", name);
return buf;
}
*/
import "C"
import "unsafe"
func main() {
name := C.CString("World")
defer C.free(unsafe.Pointer(name))
result := C.greet(name)
defer C.free(unsafe.Pointer(result))
fmt.Println(C.GoString(result))
}Linking Libraries
// #cgo LDFLAGS: -lssl -lcrypto
// #include <openssl/sha.h>
import "C"Downsides
- No cross-compilation without toolchain
- CGO_ENABLED=1 required
- Performance overhead at boundaries
- Complicates deployment
When to Use
✅ Use for: - System libraries (OpenSSL, SQLite) - Hardware interfaces - Legacy code integration
❌ Avoid for: - Simple functionality - When pure Go libraries exist - Cross-platform tools
Summary
| Task | Approach |
|---|---|
| Call C | Import pseudo-package “C” |
| Pass string | C.CString() / C.GoString() |
| Free memory | C.free(unsafe.Pointer(...)) |
| Link library | #cgo LDFLAGS: -lname |