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

    Arquivos protegidos representando extração segura de TAR com Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    tarfile extraction_filter: extraia TAR com segurança

    Aprenda tarfile extraction_filter no Python para extrair arquivos TAR com validação, segurança e controle de caminhos.

    Ler mais

    Tempo de leitura: 6 minutos
    08/10/2026
    Código Python exibindo avisos controlados com catch_warnings
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    catch_warnings: capture warnings em testes Python

    Aprenda catch_warnings no Python para capturar, testar e controlar avisos com filtros específicos e segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código Python representando referências persistentes do pickle
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    pickle persistent_id: serialize referências externas

    Aprenda pickle persistent_id no Python para serializar referências externas com IDs estáveis, validação, segurança e compatibilidade.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código binário representando buffers e memória no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    memoryview.count: conte valores sem copiar buffers

    Aprenda memoryview.count no Python para contar bytes e valores em buffers sem cópias, com formatos, limites e boas práticas.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026
    Código Python com anotações de tipos
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    annotationlib: evite imports circulares em anotações

    Aprenda annotationlib no Python para ler anotações adiadas, evitar imports circulares e criar ferramentas de introspecção seguras.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026
    Código Python em tela representando inspeção de módulos e pacotes
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    inspect.ispackage: identifique pacotes Python

    Aprenda inspect.ispackage no Python para identificar pacotes, explorar módulos e criar ferramentas de introspecção seguras.

    Ler mais

    Tempo de leitura: 5 minutos
    05/10/2026