Stage 1 · Code
Advanced Runtime & Tooling
Calling C Code with cgo
Understand how Go can cross the language boundary into C, why that can be valuable, and why teams treat cgo as a deliberate trade-off rather than a free convenience.
Why cgo Exists
Go was designed to be pleasant as a self-contained language, but real systems rarely start from zero. Companies already have C libraries. Operating systems expose APIs first in C. Hardware vendors often ship C headers before anything else. cgo exists so a Go program can reuse that world instead of pretending it does not exist.
The most common reasons to use cgo are practical, not fashionable: reusing an existing C library you trust, or reaching an OS-level capability that has no solid pure-Go equivalent. In other words, cgo is usually about interop, not about writing ordinary application logic in a more complicated way.
Normal Go code stays inside one language, one compiler toolchain, and one runtime model. cgo builds a bridge to C so your Go code can call C functions and work with C types when you genuinely need that connection.
The Anatomy of import C
cgo uses a special pseudo-package named C. You do not download it like a normal dependency. Instead, Go treats import "C" as a signal that this file contains C interop. The C code usually lives in a comment block directly above that import, and cgo exposes those C functions and types to the Go part of the file.
input: 21
result: 42A Go binary is calling into a C function, receiving the result, and continuing in Go again. That is the whole idea of cgo in one tiny example.
- The comment block above
import "C"is special. cgo reads it as C source or declarations. C.double_numberis not an ordinary Go identifier. It is a bridge symbol generated by cgo.- Type conversion matters because Go types and C types are similar in some cases but not identical contracts.
The Real Cost of Crossing the Boundary
Calling C from Go is not free. Each transition crosses a language boundary with different calling conventions, different memory-management assumptions, and more toolchain work. Even if the call itself is simple, the build and deployment story becomes more complicated.
| Why teams use cgo | What they pay for it |
|---|---|
| They can reuse a proven C library instead of rewriting it | Calls across the Go/C boundary are slower than ordinary Go-to-Go calls |
| They can reach low-level system APIs that have no good pure-Go option | Builds now depend on a working C toolchain and often on external headers or libraries |
| They can integrate with existing native codebases | Cross-compilation becomes much harder because you need the right native toolchain for the target platform |
This is why many Go teams treat cgo as a boundary to isolate. They keep it in a small package, expose a normal Go API above it, and avoid spreading C-facing types through the rest of the codebase.
What CGO_ENABLED Controls
CGO_ENABLED is an environment variable that tells the Go toolchain whether cgo support is enabled for the build. When it is 1, files that use import "C" are allowed and cgo participates in the build. When it is 0, cgo is disabled, which is common when people want simpler, fully static, or easier-to-cross-compile binaries.
One reason Go became popular in infrastructure work is that pure-Go binaries are easy to build and move around. Every time you introduce cgo, you give up a little of that simplicity in exchange for native interop.
Quiz
What is the main job of cgo?
Summary
- cgo lets Go programs call C code through the special
import "C"mechanism. - The C declarations or functions usually live in the comment block directly above that import.
- cgo is valuable for reusing existing C libraries and reaching native APIs, but it introduces build, portability, and performance costs.
CGO_ENABLEDcontrols whether the Go toolchain includes cgo support for a given build.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.