Compilação Separada


A diretiva #include

A diretiva #include nome_de_arquivo faz com que o texto contido num arquivo seja copiado (ou "incluído") num programa durante a compilação. Se o nome aparecer entre aspas duplas, esse arquivo será localizado a partir do diretório corrente. Caso esse nome apareça entre < e >, o arquivo a ser incluído será procurado pelo compilador no diretório onde ficam as definições das bibliotecas utilizadas pelo mesmo. Exemplos:

   
	#include <stdio.h>
	#include "busca.h"

No primeiro exemplo, o arquivo a ser incluído será o arquivo denominado "stdio.h", localizado no diretório onde o compilador normalmente procura as definições de biblioteca. No segundo exemplo, será incluído o arquivo "busca.h", localizado no diretóro corrente.

O compilador C não impõe nenhuma restrição quanto ao nome e nem quanto ao conteúdo dos arquivos incluídos durante a compilação. Além disso a diretiva #include pode aparecer em qualquer ponto do programa.

A experiência de uso da linguagem durante anos disseminou uma disciplina que é utilizada atualmente pela maioria dos programadores e estimulada pelas práticas padrão de programação. Essa disciplina estabelece o seguinte:

O protótipo de uma função

Em C, para que se possa usar uma variável, constante ou função, a mesma deve ter sido previamente definida. No caso de uma função, essa "definição" pode ser feita de duas formas:

Exemplos de protótipos para funções:

   
int busca(char *, struct linha *, int);

void teste(char *);

A implementação das funçõe declaradas acima poderia ser a seguinte:

   
int busca(char* nome, struct linha* tab, int n)
{
  int cond;
  int esq, dir, m;

  esq = 0;
  dir = n - 1;
  while (esq <= dir) {
    m = (esq + dir) / 2;
    if((cond = strcmp(nome, tab[m].nome)) < 0) dir = m - 1;
    else if(cond > 0) esq = m + 1;
         else return m;
  }
  return -1;  /* nao achou ! */
}

void teste(char* nome)
{
  int b;
  printf("nome: %s   ramal: ",nome);
  if((b = busca(nome,tabela,N))== -1) printf("desconhecido\n");
  else printf(" %d \n", tabela[b].numero);
}

Um exemplo completo

No exemplo apresentado a seguir, o mesmo programa de busca em tabelas é implementado através de 3 módulos:

Os arquivos "bin.h" e "bin.c" contém o seguinte:

   
// ==========================================
// bin.h
// =========================================
struct linha 
       {
	 char * nome;
	 int    numero;
       };

int busca(char *, struct linha *, int);
// ===========================================
// bin.c
// ==========================================

#include "bin.h"

int busca(char* nome, struct linha* tab, int n)
{
  int cond;
  int esq, dir, m;

  esq = 0;
  dir = n - 1;
  while (esq <= dir) {
    m = (esq + dir) / 2;
    if((cond = strcmp(nome, tab[m].nome)) < 0) dir = m - 1;
    else if(cond > 0) esq = m + 1;
         else return m;
  }
  return -1;  /* nao achou ! */
}

Os arquivos "tab.h" e "tab.c" contém o seguinte:

   
// ==================================
// tab.h
// ==================================

extern struct linha tabela[];
int tamanho();
// =========================================
// tab.c
// =========================================

#include "bin.h"
#include "tab.h"

#define N 17

struct linha tabela[N] = { 
                          {"Angela",   323 },
			  {"Antonio",  423 },
			  {"Beatriz",  524 },
			  {"Carla",    623 },
                          {"Carlos",   324 },
			  {"Denise",   624 },
			  {"Eduardo",  326 },
			  {"Fernanda", 523 },
			  {"Fernando", 223 },
			  {"Francisco",331 },
			  {"Giselle",  231 },
			  {"Heitor",   443 },
			  {"Ingrid",   334 },
			  {"Joao",     211 },
			  {"Jose",     324 },
			  {"Leticia",  536 },
			  {"Luis",     634 }
                         };

int tamanho(){ return N; }

É importante notar que no arquivo "tab.h" a variável tabela está sendo definida como extern, o que significa que a mesma deve ter sido declarada em outro módulo. Essa declaração está sendo feita em "tab.c".

O arquivo "tstbin.c", correspondente ao programa principal tem o seguinte conteúdo:

   
// ===================================
// tstbin.c
// ===================================

#include <string.h>
#include "bin.h"
#include "tab.h"


void teste(char *);

int main()
{
  teste("Eduardo");
  teste("Joao");
  teste("Angela");
  teste("Joaquim");
} 


void teste(char* nome)
{
  int b;
  printf("nome: %s   ramal: ",nome);
  if((b = busca(nome,tabela,tamanho()))== -1) printf("desconhecido\n");
  else printf(" %d \n", tabela[b].numero);
}

O uso do gcc em compilação separada

A opção -c do gcc indica que o programa deve apenas compilado, gerando um módulo objeto. Exemplos:

   
	gcc -c bin.c
	gcc -c tab.c

Esses comandos causam a compilação dos módulos "bin.c" e "tab.c", gerando os módulos objeto correspondentes, "bin.o" e "tab.o". Para compilar o módulo principal, "tstbin.c", que deve ser ligado aos módulos objetos "bin.o" e "tab.o", devemos acionar o gcc através do comando abaixo:

	gcc -o tstbin  bin.o  tab.o  tstbin.c

Nessa linha de comando, a opção "-o" está sendo usada para indicar o nome do executável gerado, os módulos objeto "bin.o" e "tab.o" estão sendo passados como parâmetros e "tstbin.c" corresponde ao módulo principal a ser compilado.