O mais simples
A forma mais simples é:
cc prog.c
Que no caso compila o programa prog.c, sem qualquer otimização, deixando a tabela de símbolos (que contém a descrição das variáveis, funções etc), e faz a saída no arquivo a.out.
O comando strip remove esta tabela de símbolos:
strip a.out
Esta tabela de símbolos pode ser usado na depuração do programa, e alguns compiladores tem opções para gerar tabelas bem mais detalhadas para usar na depuração.
Mas que tal já gerar o arquivo sem esta tabela de símbolos?
cc -s prog.c
A opção -s já diz que o arquivo final não terá a tabela de símbolos.
Mas posso escolher o nome do arquivo de saída? Claro que sim, com a opção -o (note bem, letra o minúsculo), seguida pelo nome desejado:
cc -s -o prog prog.c
Neste exemplo, ao invés de gravar o programa executável no arquivo a.out, este será escrito no arquivo prog.
Mas o código não está bom, pois não está otimizado. Para isto usamos a opção -O (note, aqui é maiúsculo):
cc -s -O -o prog prog.c
A maioria dos compiladores tem níveis de otimização, que são escolhidos colocando um número depois da opção -O. Normalmente , quanto maior o número, maior é o nível de otimização.
cc -s -O3 -o prog prog.c
Neste ponto você já consegue compilar o seu programa, mas ainda não viu nada sobre passos de compilação. Para entender melhor como funciona a compilação, continue lendo.
Passos de compilação
A compilação do programa não acontece de uma vez só. Ela é feita em etapas, chamadas de passos. O programa cc basicamente organiza os passos, segundo as opções, e chama vários programas, cada um responsável por um passo, passando os parâmetros adequados.
Abaixo estão, na ordem, os programas que são chamados durante a compilação de um programa. Nota, segundo a implementação, esta ordem pode variar em forma, em quantidade, podem ter fusões de etapas etc.
cpp: C Preprocessor
Ele é o processador de macros responsável por tratar todas as compilações condicionais (#ifdef, #if, #ifndef, #else e #endif), as macros (#define), inclusões (#include) e qualquer outra coisa relacionada com linhas que comecem com #. É ele que faz as substituições das macros.
cc1: Primeiro passo de compilação
Este programa pega o programa fonte modificado pelo preprocessador e compila, gerando um código em Assembler, com a extensão .s.
cc2: Otimização
Se não foi pedida nenhuma otimização, este passo não é executado, mas se foi pedida, este programa é executado, pegando a saída do programa anterior e otimizando o código, A saída é um outro arquivo .s.
as: Montador Assembler
O Montador Assembler pega o arquivo .s, que é o programa fonte convertido para Assembler, e faz a tradução para um Arquivo Objeto Realocável, com extensão .o. Este arquivo já é binário, em código de máquina, e tem a tabela de símbolos mencionada anteriormente. Mas a tabela de símbolo aqui é fundamental, pois descreve o que ainda vai ser necessário pegar da biblioteca para terminar a compilação.
Se ao chamar o cc for passada a opção -c, a compilação irá parar neste ponto, não executando a etapa seguinte. Isto será usado em um artigo futuro.
Já usei este programa quando brincava com Assembler do PDP 11/70, e a saída já era executável quando não dependia de nada de nenhuma biblioteca.
ld: Link edição
Última etapa. Tecnicamente o programa está compilado a um bom tempo, mas não está pronto para ser executado.
Este programa pega todos os Arquivos Objetos Realocáveis (.o), sejam eles criados por você ou vindos das bibliotecas, que compõem o programa e os junta, ligando as referências entre si, resolvendo os endereços das funções, variáveis extern etc.
Por exemplo. Se o seu programa usa a função printf(), então no Arquivo Objeto Realocável do seu programa, que saiu do as, tem a informação que precisa que o endereço da printf() seja colocado nos pontos onde ela é chamada. O ld procura nas bibliotecas pelo Arquivo Objeto Realocável que contenha a printf() e copia para o Programa Executável que está gerando, resolvendo os endereçamentos.
A opção -s do cc é tratada nesta etapa. O cc repassa a opção para o ld, informando que a tabela de símbolos não deve ser gravada no arquivo final.
Compilando programa com vários arquivos fontes
Programas grandes normalmente são divididos em vários arquivos fontes, para facilitar o trabalho de programação. Cada um pode ser feito por um programador, cada um pode tratar de uma etapa de processamento etc. Como estes programas são compilados? Normalmente usando o programa make para ajudar, que será assunto de um outro artigo, mas de qualquer forma a ideia é derivada do que vou apresentar aqui.
cc -s -O3 -o prog principal.c parte1.c parte2.c parte3.c parte4.c
No exemplo acima, o programa prog é composto por um arquivo principal e mais outros 4 arquivos. Neste caso, todas as 5 partes são compiladas independentemente até a etapa do as. Então os cinco arquivos .o, os cinco Arquivos Objetos Realocáveis, são passados ao ld, que fará a junção deles, mais o que eles precisarem das bibliotecas, em um Programa Executável, um Arquivo Objeto Executável.
Conclusão
Aqui tratei como a se usa o compilador, como são os seus passos, e como compilar um programa com vários arquivos fontes. Faltou falar do make, que torna a compilação de programas muito grandes, com muitos fontes, prática e rápida, mas estabeleci as bases que serão necessárias quando falar do make.
Sugiro a leitura, mesmo que não detalhada e sem entender muitos dos detalhes, do manual do seu compilador C. Ele pode esclarecer muitas dúvidas.
Nenhum comentário:
Postar um comentário