Criando e usando uma biblioteca própria

1. Conceitos Fundamentais de Bibliotecas em C

Em C, uma biblioteca é um conjunto de funções e símbolos compilados que podem ser reutilizados por diferentes programas. Existem dois tipos principais:

Bibliotecas estáticas (.a no Linux, .lib no Windows): o código é copiado para dentro do executável durante a linkagem. Isso gera binários maiores, mas independentes em tempo de execução.

Bibliotecas dinâmicas (.so no Linux, .dll no Windows): o código permanece em um arquivo separado e é carregado em memória quando o programa é executado. O executável fica menor e múltiplos programas podem compartilhar a mesma biblioteca.

As vantagens de criar sua própria biblioteca incluem:
- Reuso: escreva uma vez, use em vários projetos
- Modularidade: separe responsabilidades em unidades lógicas
- Encapsulamento: esconda detalhes de implementação, exponha apenas a interface

A estrutura básica consiste em arquivos de cabeçalho (.h) que definem a interface pública e arquivos de implementação (.c) que contêm o código.

2. Planejamento e Estrutura do Projeto

Vamos criar uma biblioteca simples de operações matemáticas. Primeiro, definimos a interface pública no cabeçalho:

// include/math_utils.h
#ifndef MATH_UTILS_H
#define MATH_UTILS_H

double media(double a, double b);
double desvio_padrao(double valores[], int n);
long fatorial(int n);
int eh_primo(int n);

#endif

A organização de diretórios recomendada é:

projeto/
├── include/    # cabeçalhos públicos
├── src/        # implementação
├── lib/        # bibliotecas compiladas
└── app/        # programas cliente

3. Criando uma Biblioteca Estática (.a)

Implementamos as funções no arquivo fonte:

// src/math_utils.c
#include "math_utils.h"
#include <math.h>

double media(double a, double b) {
    return (a + b) / 2.0;
}

double desvio_padrao(double valores[], int n) {
    double soma = 0.0, media, soma_dif = 0.0;
    for (int i = 0; i < n; i++) soma += valores[i];
    media = soma / n;
    for (int i = 0; i < n; i++)
        soma_dif += pow(valores[i] - media, 2);
    return sqrt(soma_dif / n);
}

long fatorial(int n) {
    long resultado = 1;
    for (int i = 2; i <= n; i++) resultado *= i;
    return resultado;
}

int eh_primo(int n) {
    if (n < 2) return 0;
    for (int i = 2; i * i <= n; i++)
        if (n % i == 0) return 0;
    return 1;
}

Compilamos para objetos e empacotamos com ar:

gcc -c -Iinclude src/math_utils.c -o src/math_utils.o
ar rcs lib/libmath_utils.a src/math_utils.o

O comando ar cria o arquivo .a. A flag r insere/substitui, c cria o arquivo se não existir, s gera o índice de símbolos.

4. Criando uma Biblioteca Dinâmica (.so)

Para criar uma versão compartilhada, compilamos com -fPIC e linkamos com -shared:

gcc -c -fPIC -Iinclude src/math_utils.c -o src/math_utils.o
gcc -shared -Wl,-soname,libmath_utils.so.1 \
    -o lib/libmath_utils.so.1.0 src/math_utils.o -lm
ln -s libmath_utils.so.1.0 lib/libmath_utils.so.1
ln -s libmath_utils.so.1 lib/libmath_utils.so

O soname define o nome lógico da biblioteca. O versionamento ajuda na compatibilidade: libmath_utils.so.1 é o soname, e libmath_utils.so é o linker name usado durante a compilação.

5. Compilação e Linkagem com a Biblioteca Própria

Agora criamos um programa cliente que usa nossa biblioteca:

// app/main.c
#include <stdio.h>
#include "math_utils.h"

int main() {
    double v[] = {10.0, 20.0, 30.0, 40.0, 50.0};

    printf("Media: %.2f\n", media(15.0, 25.0));
    printf("Desvio padrao: %.2f\n", desvio_padrao(v, 5));
    printf("Fatorial de 10: %ld\n", fatorial(10));
    printf("17 eh primo? %s\n", eh_primo(17) ? "Sim" : "Nao");

    return 0;
}

Compilamos usando flags específicas:

# Com biblioteca estática
gcc -Iinclude app/main.c -Llib -lmath_utils -lm -o app/programa_static

# Com biblioteca dinâmica
gcc -Iinclude app/main.c -Llib -lmath_utils -lm -o app/programa_dynamic
  • -Iinclude: diz onde encontrar os cabeçalhos
  • -Llib: diz onde procurar as bibliotecas
  • -lmath_utils: linka com a biblioteca (remove o prefixo lib e a extensão)
  • -lm: linka com a biblioteca matemática (necessário para pow e sqrt)

6. Configuração do Ambiente para Uso da Biblioteca

Para bibliotecas dinâmicas, o sistema precisa saber onde encontrá-las em tempo de execução. Três opções principais:

Variável LD_LIBRARY_PATH (temporário):

export LD_LIBRARY_PATH=/caminho/para/lib:$LD_LIBRARY_PATH
./app/programa_dynamic

Instalação em diretório do sistema (permanente):

sudo cp lib/libmath_utils.so.1.0 /usr/local/lib/
sudo ldconfig

O comando ldconfig atualiza o cache de bibliotecas e cria os links simbólicos necessários.

Linkagem com rpath (embutido no executável):

gcc -Iinclude app/main.c -Llib -lmath_utils -lm \
    -Wl,-rpath,'$ORIGIN/../lib' -o app/programa_dynamic

7. Depuração e Boas Práticas Avançadas

Para verificar os símbolos exportados pela biblioteca:

nm -D lib/libmath_utils.so
objdump -T lib/libmath_utils.so

O comando nm lista símbolos. Símbolos marcados com T são funções definidas na biblioteca. Símbolos U são referências não resolvidas (dependências externas).

Versionamento semântico: use o formato MAJOR.MINOR.PATCH. Mude o major quando quebrar compatibilidade, minor para novas funcionalidades compatíveis, patch para correções.

Compatibilidade retroativa: nunca remova ou altere a assinatura de funções públicas existentes. Adicione novas funções com nomes diferentes.

8. Exemplo Completo: Do Código ao Uso Final

Vamos criar um Makefile que automatiza todo o processo:

# Makefile
CC = gcc
CFLAGS = -Wall -Wextra -Iinclude
LDFLAGS = -Llib -lmath_utils -lm

all: lib/libmath_utils.a lib/libmath_utils.so app/programa

lib/libmath_utils.a: src/math_utils.o
    mkdir -p lib
    ar rcs $@ $^

lib/libmath_utils.so: src/math_utils.o
    mkdir -p lib
    $(CC) -shared -Wl,-soname,libmath_utils.so.1 \
        -o $@.1.0 $^ -lm
    ln -sf libmath_utils.so.1.0 lib/libmath_utils.so.1
    ln -sf libmath_utils.so.1 lib/libmath_utils.so

src/math_utils.o: src/math_utils.c include/math_utils.h
    $(CC) -c -fPIC $(CFLAGS) $< -o $@

app/programa: app/main.c lib/libmath_utils.a
    $(CC) $(CFLAGS) $< $(LDFLAGS) -o $@

clean:
    rm -rf src/*.o lib/ app/programa

.PHONY: all clean

Para usar:

make
./app/programa

A saída esperada é:

Media: 20.00
Desvio padrao: 14.14
Fatorial de 10: 3628800
17 eh primo? Sim

Criar sua própria biblioteca em C é uma habilidade fundamental para projetos maiores. Ela permite organizar código de forma profissional, facilitar manutenção e promover reuso entre diferentes programas. Comece com bibliotecas pequenas e vá evoluindo conforme sua experiência cresce.

Referências