ctypes no Python: use bibliotecas C

Publicado em: 24/08/2026
Tempo de leitura: 6 minutos
A developer typing code on a laptop with a Python book beside in an office.

O módulo ctypes permite carregar bibliotecas compartilhadas e chamar funções C diretamente do Python. Ele fornece tipos compatíveis com C, ponteiros, estruturas, unions, arrays, callbacks e acesso a símbolos exportados por DLLs, arquivos .so e .dylib.

Esse poder vem com risco elevado. ctypes opera sobre memória nativa e contorna várias proteções do Python. Uma assinatura errada, ponteiro inválido ou callback coletado pelo garbage collector pode corromper dados, revelar memória, causar access violation ou encerrar o processo.

Quando usar ctypes

Use ctypes quando uma biblioteca C está disponível, não existe binding mantido e a API é estável o suficiente para ser descrita com precisão. Para APIs grandes, complexas ou críticas, considere gerar bindings com CFFI, Cython, pybind11 ou uma extensão C dedicada.

Antes de escrever o wrapper, confirme ABI, arquitetura, calling convention, tamanho dos tipos, ownership de memória, thread safety e política de erros.

Encontrar e carregar uma biblioteca

from ctypes import CDLL
from ctypes.util import find_library

nome = find_library("m")
if not nome:
    raise RuntimeError("Biblioteca matemática não encontrada")

libm = CDLL(nome)

find_library() depende da plataforma. Em wrappers distribuídos, muitas vezes é melhor definir nomes e caminhos testados por sistema operacional em vez de aceitar qualquer arquivo fornecido pelo usuário.

Carregar uma biblioteca executa código nativo dentro do processo. Não permita upload ou caminho arbitrário para CDLL(). Verifique origem, assinatura e permissões.

Calling conventions

CDLL usa a convenção C padrão. No Windows, WinDLL usa stdcall e OleDLL interpreta retornos como HRESULT. Escolher a convenção errada altera a pilha e pode causar falhas.

Consulte o header C e a documentação da biblioteca. Não deduza a convenção pelo nome do arquivo.

Defina argtypes e restype

Funções são assumidas como retornando c_int quando restype não é definido. Essa suposição pode truncar ponteiros e inteiros de 64 bits.

from ctypes import CDLL, c_double
from ctypes.util import find_library

libm = CDLL(find_library("m"))
cos = libm.cos
cos.argtypes = [c_double]
cos.restype = c_double

print(cos(0.0))

Defina protótipos antes da primeira chamada. argtypes valida e converte argumentos; restype interpreta corretamente o retorno.

Tipos C compatíveis

O módulo oferece c_int, c_uint, c_long, c_size_t, c_float, c_double, c_char_p, c_wchar_p, c_void_p e muitos outros. O tamanho de tipos como long varia entre plataformas.

Para protocolos binários, compare com o guia de struct no Python. struct é adequado para bytes com layout definido; ctypes.Structure tenta reproduzir o layout ABI do compilador.

Strings e buffers mutáveis

c_char_p aponta para string terminada em NUL e não deve ser usado como buffer mutável. Para uma função que escreve dados, crie um buffer.

from ctypes import create_string_buffer

buffer = create_string_buffer(256)
# função_c(buffer, len(buffer))
print(buffer.value)

Confirme se o tamanho inclui o terminador NUL e como a API comunica truncamento. Nunca entregue um buffer menor que o tamanho informado à função C.

Ponteiros e byref()

byref() passa um objeto por referência sem criar um ponteiro Python completo. pointer() cria um objeto de ponteiro reutilizável.

from ctypes import c_int, byref

resultado = c_int()
# status = biblioteca.calcular(10, byref(resultado))
# print(status, resultado.value)

ctypes detecta ponteiro NULL ao desreferenciar, mas não consegue validar um endereço não nulo incorreto. Indexar além de um array pode ler ou alterar memória arbitrária.

Estruturas e alinhamento

from ctypes import Structure, c_int

class Point(Structure):
    _fields_ = [
        ("x", c_int),
        ("y", c_int),
    ]

O layout depende da ABI. Use sizeof(), offsets dos campos e testes contra um programa C de referência. _pack_, _align_ e _layout_ alteram o layout e não devem ser ajustados por tentativa.

Bit fields e unions variam entre compiladores. A documentação recomenda passar estruturas com bit fields e unions por ponteiro, não por valor.

Ownership de memória

Uma função C pode retornar memória estática, um ponteiro que deve ser liberado, ou um buffer pertencente a outro objeto. O wrapper precisa documentar quem é responsável pela liberação e qual função deve ser usada.

Nunca libere memória alocada por uma biblioteca com um allocator diferente. Em especial no Windows, runtimes C diferentes podem ter heaps incompatíveis.

lib.criar_buffer.restype = c_void_p
lib.liberar_buffer.argtypes = [c_void_p]

ponteiro = lib.criar_buffer()
if not ponteiro:
    raise MemoryError("Falha ao alocar buffer nativo")
try:
    pass  # use o ponteiro com limites conhecidos
finally:
    lib.liberar_buffer(ponteiro)

Erros com errno

Carregue a biblioteca com use_errno=True quando a API usa errno. Depois da chamada, consulte get_errno().

import os
from ctypes import CDLL, get_errno

lib = CDLL("libexemplo.so", use_errno=True)
resultado = lib.operacao()
if resultado == -1:
    codigo = get_errno()
    raise OSError(codigo, os.strerror(codigo))

O próximo conjunto deste lote abordará o módulo errno em detalhes.

errcheck

O atributo errcheck centraliza a interpretação do retorno.

def verificar(resultado, funcao, argumentos):
    if resultado == 0:
        codigo = get_errno()
        raise OSError(codigo, os.strerror(codigo))
    return resultado

lib.operacao.errcheck = verificar

Confirme o contrato da API: algumas funções usam zero como sucesso, outras usam zero como falha ou retornam ponteiro NULL.

Callbacks

CFUNCTYPE e WINFUNCTYPE transformam uma função Python em ponteiro chamável por C.

from ctypes import CFUNCTYPE, c_int

COMPARADOR = CFUNCTYPE(c_int, c_int, c_int)

@COMPARADOR
def comparar(a, b):
    return (a > b) - (a < b)

Mantenha uma referência Python ao callback enquanto o código C puder chamá-lo. Caso contrário, ele pode ser coletado e a próxima chamada causará crash.

Exceções não devem escapar de callbacks. Capture-as, registre um sinal de erro e retorne um valor definido pelo contrato C.

Threads e GIL

CDLL normalmente libera o GIL durante chamadas nativas. Isso não torna a biblioteca thread-safe. Proteja estados compartilhados conforme a documentação C.

Em builds free-threaded do Python, acessos concorrentes à mesma memória por objetos de ponteiro diferentes exigem sincronização explícita. Use threading.Lock.

Segfaults e diagnóstico

Um segfault não é uma exceção Python comum. Ative faulthandler, execute testes em subprocesso e use ferramentas como AddressSanitizer, Valgrind ou debugger nativo.

Para funções de alto risco, isole o wrapper em outro processo. Se a biblioteca falhar, o serviço principal pode reiniciar o worker sem perder o processo inteiro.

Validação de limites

Antes de chamar C, valide tamanho, intervalo e consistência. Não confie em casts que silenciosamente truncam inteiros. Verifique que contadores cabem no tipo C escolhido e que arrays têm ao menos o número de elementos informado.

Evite cast() sem necessidade

cast() reinterpreta o mesmo endereço como outro tipo. Ele não converte conteúdo nem valida alinhamento. Use somente quando o contrato C exige e o layout está comprovado.

Testes multiplataforma

Teste Windows, Linux e macOS quando suportados, em arquiteturas de 64 bits e versões reais da biblioteca. Verifique sizeof(), endianness, calling convention, símbolos, encoding e mensagens de erro.

O guia de inspect no Python pode ajudar a validar a camada Python, mas não inspeciona a ABI nativa. Testes contra headers e versões da biblioteca são indispensáveis.

Segurança da cadeia de fornecimento

Bibliotecas nativas executam com os privilégios do processo. Fixe versões, valide hashes, use fontes confiáveis e limite os diretórios de busca. Variáveis como LD_LIBRARY_PATH podem alterar qual arquivo é carregado.

Para verificar binários distribuídos, veja hashlib no Python.

Erros comuns

Os erros mais frequentes são omitir argtypes, aceitar o restype padrão, passar string imutável como buffer, usar calling convention errada, perder a referência do callback, liberar memória com allocator incorreto, indexar ponteiros sem tamanho e carregar DLL de caminho não confiável.

Conclusão

ctypes permite criar wrappers nativos sem compilar uma extensão Python, mas exige disciplina semelhante à programação C. Defina protótipos, valide ABI e tamanhos, documente ownership, mantenha callbacks vivos e trate crashes como possibilidade real.

Consulte a documentação oficial de ctypes e a documentação oficial sobre extensões e integração com C.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    A laptop screen showing a code editor with visible programming code in a dimly lit environment.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    expat no Python: parser XML de baixo nível

    Aprenda expat no Python para parsing XML de baixo nível, handlers, namespaces, erros e proteções contra amplificação e DoS.

    Ler mais

    Tempo de leitura: 5 minutos
    23/08/2026
    A laptop screen showing a code editor with visible programming code in a dimly lit environment.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    ElementInclude no Python: use XInclude

    Aprenda ElementInclude no Python para usar XInclude com loaders seguros, base URL, profundidade máxima e bloqueio de caminhos externos.

    Ler mais

    Tempo de leitura: 5 minutos
    23/08/2026
    A developer typing code on a laptop with a Python book beside in an office.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    xmlreader no Python: controle parsers SAX

    Aprenda xmlreader no Python para configurar parsers SAX, InputSource, parsing incremental, atributos, locators e segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    23/08/2026
    Close-up view of a computer screen displaying code in a software development environment.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    saxutils no Python: utilitários para XML

    Aprenda saxutils no Python para escapar XML, preparar atributos, gerar documentos, criar filtros SAX e evitar erros de contexto.

    Ler mais

    Tempo de leitura: 6 minutos
    23/08/2026
    A developer typing code on a laptop with a Python book beside in an office.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    pulldom no Python: DOM parcial para XML

    Aprenda pulldom no Python para processar XML por eventos, expandir apenas subárvores necessárias e reduzir memória com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    23/08/2026
    Close-up view of a computer screen displaying code in a software development environment.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    xml.sax no Python: processe XML em eventos

    Aprenda xml.sax no Python para processar XML por eventos com baixo uso de memória, namespaces, handlers, limites e segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    22/08/2026