por Emilio Eduardo » 12 Jan 2013 17:22
[Atenção: Isso não está completo, possui só metade da informação e ainda vai ser revisado]
Eu sempre achei complicado entender o básico sobre esse processo de separar um programa em arquivos, mas depois que aprendi vi que é simplesmente genial e muito prático.
Acho que a chave de tudo é entender o processo de "Compilação" como um todo, entre aspas por que na verdade Compilação é uma parte do processo inteiro que é disparado quando damos um "build".
São três processos separados:
Pré-Processamento
Compilação
Link-Edição
O resultado de um processo, vai para o outro, formando uma cadeia.
Cada um desses processos tem um papel muito importante para viabilizar o arquivo final.
O Pré-Processamento tem como objetivo interpretar as diretivas referentes a ele, aquelas como #include, #ifdef, #define e algumas outras, assim como os "//" e "/*".
O que ele faz é simplesmente pegar cada um dos arquivos do projeto, ler e fazer as devidas alterações no texto do arquivo. Uma delas é retirar todos os comentários, outra é adicionar o conteúdo(Em texto mesmo) de outro arquivo, como um copiar e colar e diversas outras como interpretar as diretivas de #if e substituir os #defines no texto.
Feito isso, o resultado do pré-processamento de cada um dos arquivos .c e .h vai para o compilador.
O processo de compilação é um tanto complicado, como vocês devem imaginar, mas o importante é que o papel dele é transformar código fonte em código objeto, uma "linguagem" muito mais próxima da linguagem de máquina. Entretanto esse processo pode deixar várias coisas faltando para ser executado.
Isso acontece por que você pode chamar uma função pertencente a um arquivo a partir de outro mas o processo de compilação ocorre arquivo por arquivo, sem juntar os arquivos propriamente ditos. Dessa maneira, quando o compilador encontra o protótipo de uma função que está definida em outro arquivo ele deixa um aviso dentro do arquivo objeto dizendo: "Em algum outro arquivo do projeto deve existir uma função chamada tal, com retorno tal e com argumentos tais".
Isso também pode acontecer quando existem variáveis globais que devem ser acessadas por funções em arquivos diferentes. Para isso existem a expressão "extern" ao declarar uma variável. Isso possibilita que exista somente uma variável com determinado nome e não várias (declaradas em cada arquivo). Funciona da seguinte maneira:
Você declara a variável como : "int a" em um arquivo
E nos outros você diz ao compilador: "hey, existe essa mesma variável em algum outro lugar, deixa pro link editor que ele vai encontrar". Isso é feito pela expressão "extern".
Resumindo, o processo de compilação traduz o código-fonte para código-objeto, isso se faz arquivo por arquivo, assim como o pré-processamento. Entretanto quando a compilação está acontecendo nem todas as variáveis e funções utilizadas estão no arquivo em questão, isso faz com que o compilador deixe "lacunas" no arquivo objeto que serão posteriormente conectadas pelo link-editor.
O processo de link-edição:
O processo de link-edição tem como matéria prima o código objeto e como produto o código de máquina. O trabalho dele é pegar todos os arquivos de código objeto e juntá-los, interligando as variáveis declaradas com extern com a variável declarada normalmente e também interligando as funções utilizadas em um arquivo e definidas em outro. Além disso, ele posiciona corretamente a função main dentro do arquivo, o que faz ela ser executada por primeiro.
Existe ainda mais um pequeno conceito a ser aprendido antes de podermos botar a mão na massa:
A diferença entre declaração e definição.
Uma declaração diz ao compilador que existe uma variável ou função com aquele nome, mas ainda não explica exatamente o que ela faz nem separa espaço na memória para ela, serve exatamente para dizer ao compilador deixar pra lá que em algum outro lugar isso vai ser encontrado. A declaração de uma função é feita pela utilização de protótipos:
int soma(int a, int b); (Perceba que está presente somente o nome da função e como chamá-la, mas não o código)
Já a declaração de uma variável utiliza a expressão extern:
extern int a, isso diz que algum outro lugar a variável vai ser definida e que nesse arquivo a variável global "a" é a mesma que a daquele outro.
Já a definição define o que uma função faz ou diz que o espaço para uma variável deve ser guardado na memória.
A definição de uma função é feita como de praxe:
int soma(int a, int b){
return a+b;
}
A definição de uma variável também:
int a; //Isso diz ao compilador: Guarda um espaço na memória de tanto espaço que eu vou usar nesse arquivo.
Com essas informações, vocês podem ter uma visão mais geral do que exatamente acontece na separação de arquivos. Mas ainda pode estar muito nebuloso como fazê-lo. Vou tentar então clarear as coisas. Mas agora tenho que sair e mais tarde volto para explicar em outro post. Também quero dar uma lida nesse tópico para tentar melhorar o texto..
Valeu pessoal!