tezvyn:

Cgo: The Bridge Between Go and C Code

AI-drafted, machine-checkedSource: pkg.go.devintermediate

Cgo is Go's bridge to the C world, letting you call C functions and use C types from your Go code. It's for leveraging existing C libraries or low-level OS APIs. The footgun: cgo calls have high overhead and break Go's simple cross-compilation.

WHY IT EXISTS Go is a modern language, but decades of powerful, optimized C libraries exist for everything from graphics to scientific computing. Cgo was created to avoid reinventing the wheel, providing a mechanism for Go programs to leverage this vast ecosystem of existing C code.

THE MENTAL MODEL Think of cgo as a translation layer or a bridge between two different worlds: the Go runtime and the C execution environment. Crossing this bridge isn't free. Each call pays a performance "toll" to switch between Go's goroutine scheduler and the system's thread model, and to translate data types between the two languages.

HOW IT WORKS You enable cgo by importing a pseudo-package named "C". Immediately before this import, you write a multi-line comment that acts as a "preamble." This preamble contains standard C code, like #include directives for C headers. From your Go code, you can then access C functions, types, and variables using the C. prefix, for example C.puts() or C.size_t. You can also pass compiler and linker flags within the preamble using special #cgo directives, like "// #cgo CFLAGS: -g" or "// #cgo LDFLAGS: -lm".

WHEN TO USE IT Use cgo when you absolutely need to interface with an existing C library that has no pure Go equivalent. This is common for database drivers (like SQLite), GUI toolkits, specialized hardware drivers, or when you need to call low-level operating system APIs not exposed by the Go standard library.

WHEN NOT TO USE IT Avoid cgo if a pure Go alternative exists. The performance overhead of cgo calls can be substantial, making it a poor choice for performance-critical inner loops. Furthermore, cgo breaks Go's simple cross-compilation model. A pure Go program can be cross-compiled easily, but a cgo-enabled program requires a full C cross-compilation toolchain for the target, which is much more complex to set up. Passing pointers between Go and C is also complex due to Go's garbage collector.

ONE CANONICAL EXAMPLE A common use is calling a function from the standard C library. To print a string using C's puts function, you must first convert the Go string to a C-compatible string, as they are not the same. The code looks like this: // #include <stdio.h> import "C" import "unsafe" func main() { cString := C.CString("Hello from C!") defer C.free(unsafe.Pointer(cString)) C.puts(cString) } This shows importing "C", including the header, converting the string, calling the C function, and freeing the C-allocated memory.

Read the original → pkg.go.dev

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.