CGO: Cruzando a Fronteira Go-C
O Go compila para um binário autocontido por padrão, mas cgo permite que um pacote chame código C e linke contra bibliotecas nativas.
Busque em todas as páginas da documentação
O Go compila para um binário autocontido por padrão, mas cgo permite que um pacote chame código C e linke contra bibliotecas nativas.
Essa ponte é poderosa e cara: cada travessia muda como o runtime agenda o trabalho, como você compila e como você raciocina sobre memória.
import "C" compila Go junto com C através de um shim gerado, então funções Go podem chamar C e C pode chamar Go exportado..so.syscall/x/sys puramente Go, sidecars RPC, caminhos WebAssembly e regras de segurança FFI.Um programa Go normal é compilado inteiramente pelo gc (o compilador Go) e linkado em um único binário.
cgo insere um segundo front-end de compilador: cmd/cgo lê arquivos Go que contêm import "C" e emite código de cola C.
O compilador Go então compila seu pacote junto com essa cola, e o linker C da plataforma inclui a libc e quaisquer bibliotecas que você nomear em #cgo LDFLAGS.
O modelo mental é um sanduíche de três camadas:
Código Go <--> Shim gerado por cgo <--> Biblioteca C / libc
O pseudo-pacote C no código fonte Go não é um pacote Go real.
Tipos como C.int e funções como C.sqrt são declarações que o cgo mapeia para símbolos C.
Comentários imediatamente acima de import "C" são diretivas cgo: linhas #include, #cgo CFLAGS, #cgo LDFLAGS e #define que configuram o lado C.
Sem import "C", um arquivo é puramente Go mesmo que viva ao lado de arquivos cgo.
Tags de build como //go:build cgo (e o legado // +build cgo) permitem que você envie fallbacks puramente Go quando CGO_ENABLED=0, o que é comum em CI, contêineres estáticos e compilações cruzadas.
Cada chamada cgo de Go para C é executada em uma thread de SO dedicada.
Enquanto dentro do C, essa thread não está executando código Go, então o scheduler pode gerar outra thread para que outras goroutines continuem progredindo.
Tráfego cgo pesado, portanto, aumenta a contagem de threads de SO e o custo de troca de contexto.
É por isso que um loop apertado chamando C pode limitar a taxa de transferência abaixo do que o Go puro alcança no mesmo hardware.
Cruzar a fronteira também dispara verificações de ponteiro.
O coletor de lixo do Go não deve mover memória que o C ainda referencia.
Regras documentadas em cmd/cgo restringem a passagem de ponteiros Go para C de maneiras que poderiam sobreviver à chamada ou ocultar ponteiros dentro de valores Go que não são ponteiros.
Violações causam pânico em tempo de execução em builds verificados.
Compilar com cgo habilitado requer uma toolchain C funcional (gcc ou clang no Linux, ferramentas Xcode CLI no macOS, MinGW no Windows).
CGO_ENABLED=0 desabilita o cgo inteiramente; go build falha em pacotes que importam C a menos que existam arquivos alternativos.
Linkagem estática, musl vs glibc e compilação cruzada para GOOS=linux GOARCH=arm64 a partir do macOS tornam-se tarefas de engenharia de lançamento em vez de uma única linha de go build.
/*
#include <stdio.h>
*/
import "C"
func greet(name string) {
cs := C.CString(name)
defer C.free(unsafe.Pointer(cs))
C.printf(C.CString("hello %s\n"), cs)
}O trecho mostra o padrão recorrente: converter strings Go para C (C.CString), liberar alocações C (C.free) e manter as chamadas C curtas para que os tempos de vida dos ponteiros permaneçam óbvios.
Equipes geralmente isolam cgo atrás de um pequeno pacote interno com uma API puramente Go.
Os chamadores dependem de tipos e erros Go; apenas o wrapper importa C.
Essa fronteira torna possível testar a lógica sem C na maioria dos pacotes e trocar implementações mais tarde.
| Abordagem | Força | Fraqueza | Melhor Ajuste |
|---|---|---|---|
| cgo in-process | Latência de chamada mais baixa, espaço de endereço compartilhado | Complexidade de thread + build | Caminho crítico deve permanecer in-process |
| RPC para sidecar C/C++ | Lançamento e isolamento de falhas independentes | Sobrecarga de rede/IPC | Código legado que você não pode linkar com segurança |
Porta puramente Go ou x/sys | Builds simples, compilação cruzada fácil | Custo inicial de reescrita ou cobertura incompleta | Chamadas de sistema e algoritmos com equivalentes em Go |
| Reescrever em Go | APIs idiomáticas, uma toolchain | Custo de tempo e validação | Bibliotecas com escopo gerenciável |
Revisões de segurança e cadeia de suprimentos tratam código cgo como código nativo: estouros de buffer em C se tornam um problema do seu processo.
Fuzzing e sanitizers (ASan/UBSan) no lado C pertencem ao mesmo nível de qualidade dos testes Go.
A observabilidade se divide entre runtimes: perfis Go mostram tempo cgo sob runtime.cgocall, mas hotspots C precisam de pprof na biblioteca C ou profilers externos.
Para contêineres, documente se a imagem precisa de libc, libstdc++ ou arquivos .so de fornecedores e se você envia variantes CGO_ENABLED=0 para imagens scratch.
C.CString aloca no heap C; você deve liberá-lo com C.free (ou passar a propriedade para C com um contrato documentado).CGO_ENABLED=0 falham a menos que você escolha drivers alternativos.Qualquer arquivo Go com import "C" e o bloco de comentários cgo acima dele.
A ferramenta go invoca cmd/cgo automaticamente durante go build e go test.
Sim - apenas arquivos que importam C participam do cgo.
Mantenha os arquivos com import "C" poucos e empurre a lógica para irmãos puramente Go.
O scheduler Go não pode preempir código C arbitrário com segurança.
cgo executa C em uma thread que o runtime gerencia para que goroutines e sinais ainda se comportem de forma previsível.
Uma variável de ambiente que, por padrão, é 1 quando uma toolchain C é detectada.
Defina CGO_ENABLED=0 para forçar builds puramente Go para binários estáticos e CI mais simples.
Não - muitas chamadas de sistema são encapsuladas em Go puro (golang.org/x/sys) ou na biblioteca padrão.
Recorra ao cgo quando não houver um wrapper Go seguro ou um fornecedor que envie apenas um SDK C.
Arquivos //go:build cgo compilam apenas quando o cgo está ativado; arquivos !cgo fornecem stubs.
Este padrão alimenta módulos portáteis como drivers sqlite e bibliotecas de terminal.
Sim, com funções //export e uso cuidadoso de runtime.LockOSThread quando C detém estado local da thread.
Callbacks são mais difíceis do que chamadas unidirecionais - veja o guia de exportação nesta seção.
Sim - caminhos #cgo LDFLAGS: -L${SRCDIR}/lib são comuns para arquivos .a vendidos.
Você ainda é o proprietário do licenciamento e artefatos específicos da plataforma em vendor/ ou internal/.
Você precisa de builds arm64 de todos os .so e um compilador cruzado correspondente se compilar cruzado.
Artefatos puramente Go não têm esse acoplamento.
Quando uma biblioteca puramente Go mantida existe, quando o isolamento RPC é aceitável, ou quando você precisa de binários estáticos minúsculos sem libc.
Meça a latência antes de se comprometer com cgo in-process.
Perfis de CPU rotulam runtime.cgocall.
Também observe a contagem de threads e a latência de cauda sob carga; ambos aumentam quando seções C executam por muito tempo.
Para navegadores e alguns hosts de borda, sim - Go pode ter como alvo wasm com GOOS=js ou wasip1.
A integração nativa com o SO ainda requer cgo ou wrappers de chamada de sistema puramente Go.
#cgoVersões de Stack: Esta página foi escrita para Go 1.26.x (padrão Green Tea GC, modernizadores go fix - verifique o patch na compilação), chi (última versão - verifique na compilação), gin (última versão - verifique na compilação), echo (última versão - verifique na compilação), google.golang.org/grpc (última versão - verifique na compilação), sigs.k8s.io/controller-runtime (última versão - verifique na compilação), kubebuilder (última versão - verifique na compilação), tinygo (última versão - verifique os alvos de placa na compilação), wazero (última versão - verifique na compilação) e golangci-lint (última versão - verifique o conjunto de linters na compilação).
Revisado por Chris St. John·Última atualização: 18 de jul. de 2026