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 prefixolibe a extensão)-lm: linka com a biblioteca matemática (necessário parapowesqrt)
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
- GCC Documentation - Link Options — Documentação oficial do GCC sobre flags de linkagem, incluindo
-l,-Le-shared - GNU ar Manual — Manual completo do comando
arpara criação de bibliotecas estáticas - C Programming: Libraries and Linking — Tutorial interativo sobre criação e uso de bibliotecas em C, com exemplos práticos
- LD_LIBRARY_PATH – Linux Dynamic Library Path — Página man do linker dinâmico do Linux, explicando variáveis de ambiente e configuração
- Creating and Using Shared Libraries in C — Guia detalhado sobre bibliotecas compartilhadas no Linux, incluindo versionamento e soname
- Makefile Tutorial - Libraries — Tutorial de Makefile com seção específica sobre compilação e linkagem de bibliotecas