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