Bibliotecas são amplamente utilizadas na linguagem C. Além da biblioteca padrão (LibC) e das bibliotecas disponibilizadas pelo sistema operacional, o programador pode desenvolver suas próprias bibliotecas, para usar em seus projetos ou disponibilizá-las a terceiros.
As bibliotecas podem ser construídas para ligação estática ou dinâmica (DLL) com o código executável que as utiliza. Este texto explica as duas técnicas em ambiente Linux.
Para usar um exemplo concreto, consideremos a biblioteca ''hello'', que oferece funções para escrever na tela mensagens de "olá" em diversas linguagens. O código-fonte dessa biblioteca é composto pelos seguintes arquivos:
#include <stdio.h>
#include "hello.h"
void hello_pt ()
{
printf ("Ola, mundo!\n") ;
}
#include <stdio.h>
#include "hello.h"
void hello_en ()
{
printf ("Hello, world!\n") ;
}
#include <stdio.h>
#include "hello.h"
void hello_fr ()
{
printf ("Salut, le monde !\n") ;
}
O arquivo de cabeçalho também faz parte da biblioteca:
#ifndef __HELLO__
#define __HELLO__
void hello_pt () ;
void hello_en () ;
void hello_fr () ;
#endif
Um programa que utilize a biblioteca ''hello'' pode ser escrito desta forma:
#include "hello.h"
int main ()
{
hello_pt () ;
hello_en () ;
hello_fr () ;
return 0 ;
}
Para construir uma biblioteca de ligação estática são necessários vários passos. Inicialmente, todos os arquivos-fonte que compõem a biblioteca devem ser compilados, para gerar os arquivos-objeto correspondentes:
gcc -c hello_pt.c
gcc -c hello_en.c
gcc -c hello_fr.c
A seguir, deve ser usado o utilitário ''ar'' (archiver) para juntar todos os arquivos-objeto em uma biblioteca estática chamada ''libhello.a'':
ar rvs libhello.a hello_pt.o hello_en.o hello_fr.o
Os flags ''rvs'' indicam:
''r'' (replace): substituir versões anteriores dos arquivos na biblioteca, caso existam
''v'' (verbose): mostrar na tela as inclusões que estão sendo realizadas
''s'' (symbols): criar uma tabela dos símbolos que estão sendo agregados à biblioteca
O utilitário ''ar'' possui diversas outras opções (flags). Por exemplo, pode-se consultar o conteúdo de uma biblioteca estática com o comando abaixo.
ar t libhello.a
hello_en.o
hello_fr.o
hello_pt.o
Pode-se consultar todos os símbolos definidos em uma biblioteca estática (ou em qualquer arquivo objeto) através do utilitário ''nm''. man nm para a descrição da tabela.
nm libhello.a
hello-en.o:
0000000000000000 T hello_en
U puts
hello-fr.o:
0000000000000000 T hello_fr
U puts
hello-pt.o:
0000000000000000 T hello_pt
U puts
Para atualizar/incluir qualquer arquivo da biblioteca, basta executar ''ar'' novamente, indicando o(s) arquivo(s) a atualizar/incluir:
ar rvs libhello.a hello_it.o hello_es.o hello_jp.o
A forma mais simples de usar a biblioteca é indicá-la ao compilador no momento da compilação ou ligação:
gcc main.c -o main libhello.a
Uma opção abreviada de ligação pode ser utilizada. Nela, não é necessário indicar o nome completo da biblioteca:
gcc main.c -o main -L. -lhello
A opção ''-L.'' é necessária para incluir o diretório corrente nos caminhos de busca de bibliotecas do ligador. Esta abordagem é melhor que a anterior, pois neste caso o ligador somente irá incluir no executável final os objetos que forem efetivamente necessários.
Observe que a biblioteca foi informada ao ligador na opção ''-lhello''. Por default, ao encontrar uma opção ''-labc'', o ligador irá procurar pela biblioteca ''libabc.a'' nos diretórios default de bibliotecas (''/lib'', ''/usr/lib'', ''/usr/local/lib'', ...) e depois disso nos diretórios informados pela opção ''-L''.
A construção de uma biblioteca de ligação dinâmica é um pouco mais complexa. Primeiro, é necessário compilar os arquivos-fonte que irão compor a biblioteca usando a opção ''-fPIC'', que gera código binário independente de posição (PIC - Position Independent Code). Como a ligação da biblioteca ocorre durante a carga/execução, a posição de seu código na memória dos processos que irão utilizá-la não pode ser determinada previamente.
gcc -fPIC -c hello_pt.c
gcc -fPIC -c hello_en.c
gcc -fPIC -c hello_fr.c
Depois, pode-se criar a biblioteca dinâmica, a partir dos arquivos-objeto:
gcc -g -shared -Wl,-soname,libhello.so.0 -o libhello.so.0.0 hello_pt.o hello_en.o hello_fr.o
Observe que a opção ''-Wl'' transfere a opção ''-soname=libhello.so.0'' ao ligador. Essa opção permite definir o nome e versão da biblioteca.
Finalmente, para instalar a biblioteca, deve-se movê-la para o diretório adequado (geralmente ''/usr/lib'' ou ''/usr/local/lib'') e gerar os links necessários para indicar os números de versão (0) e revisão (0):
mv libhello.so.0.0 /usr/local/lib
cd /usr/local/lib
ln -s libhello.so.0.0 libhello.so.0
ln -s libhello.so.0 libhello.so
ls -l
lrwxrwxrwx 1 prof 12 Out 2 18:20 libhello.so -> libhello.so.0
lrwxrwxrwx 1 prof 14 Out 2 18:06 libhello.so.0 -> libhello.so.0.0
-rwxr-xr-x 1 prof 6914 Out 2 18:06 libhello.so.0.0
A compilação usando a biblioteca ocorre da mesma forma que no caso estático:
gcc main.c -o main -L. -lhello
./main
Caso a biblioteca esteja em um diretório não listado em ''/etc/ld.so.conf'', que é o arquivo de configuração do carregador e ligador dinâmico, deve-se incluir o diretório nesse arquivo e a seguir executar ''ldconfig'', ou informar o carregador dinâmico através da variável de ambiente ''LD_LIBRARY_PATH'':
export LD_LIBRARY_PATH=.
./main
Os processos de construção de bibliotecas e da execução com bibliotecas para ligação dinâmicas são estudados em mais detalhe na disciplina Software Básico.