À medida em que um software cresce em tamanho e funcionalidades, seu código-fonte deve ser organizado corretamente para facilitar sua compreensão, manutenção e evolução. É importante quebrar o código-fonte em arquivos separados, dividindo-o de acordo com os módulos e/ou funcionalidades do sistema.
Ao dividir o código-fonte em arquivos separados, alguns cuidados devem ser tomados:
.c
;.h
(com o mesmo nome do arquivo .c
correspondente), a ser incluso pelos arquivos que usarem essas funções;#include
) de arquivos de cabeçalho (.h
).
abobrinha.c
contendo várias funções e definições de estruturas de dados, imagine o arquivo de cabeçalho abobrinha.h
como a definição da interface a ser usada por outros arquivos C para usar as funcionalidades implementadas em abobrinha.c
.
#include “arquivo.c”
).h
)
O exemplo didático a seguir implementa um conjunto de funções que permitem definir e operar sobre números complexos.
O arquivo principal (neste exemplo, main.c
) deve incluir todos os arquivos de cabeçalho necessários para sua compilação e também deve definir a função main
:
#include "complex.h" int main() { complex_t a, b, c ; complex_define (&a, 10, 17) ; complex_define (&b, -2, 4) ; complex_sum (&c, a, b); ... }
main.c
provavelmente não define funções (ou estruturas, tipos, etc) que serão usadas em outros programas, não é necessário criar um arquivo main.h
.
Por sua vez, o arquivo de cabeçalho complex.h
deve declarar somente informações públicas: tipos de dados e protótipos de funções que devem ser conhecidas por quem irá utilizar as funcionalidades providas por complex.c
:
#ifndef __COMPLEX__ #define __COMPLEX__ typedef struct { float r,i; } complex_t ; void complex_define (complex_t *v, float r, float i) ; ... #endif
Deve-se observar o uso das macros de pré-compilação #ifdef
e #define
, para evitar a repetição das definições, caso o mesmo arquivo de cabeçalho seja incluído múltiplas vezes em diferentes locais do código.
O arquivo correspondente complex.c
conterá então as informações privadas do módulo: estruturas de dados internas, variáveis globais e implementações das funções. Esse arquivo deve incluir todos os cabeçalhos necessários à implementação das funções.
#include <math.h> #include "complex.h" // internal function, used only in this file void convert_polar_rect (float r, float a, float *x, float *y) { // implementation of the function ... } // function "exported" through complex.h void complex_define (complex_t *v, float r, float i) { // implementation of the function ... } ...
Em resumo:
complex.c
: implementação das funções de manipulação de números complexos.complex.h
: interface (protótipos) das funções definidas em complex.c
.main.c
: programa principal, que usa as funções descritas em complex.h
e implementadas em complex.c
. Para compilar:
cc -Wall main.c complex.c -o main -lm
O arquivo complex.c
também pode ser compilado separadamente, gerando um arquivo-objeto complex.o
que poderá ser ligado ao arquivo main.o
posteriormente. Essa organização torna mais simples a construção de bibliotecas e a distribuição de código binário para incorporação em outros projetos (reuso de código). Além disso, essa estruturação agiliza a compilação de grandes projetos, através do sistema Make.
Um outro aspecto importante da organização do código é o uso de declarações extern
para variáveis globais usadas em vários arquivos de código-fonte. Esta página contém uma excelente explicação sobre o uso correto da declaração extern
.