Tabela de conteúdos
Pré-Compilação
Fases de uma compilação
O processo de compilação de um programa é constituído de três fases distintas:
- pré-compilação;
- compilação;
- link-edição.
Na fase de pré-compilação, o programa fonte é lido e caso se encontre comandos do pré-compilador, eles serão processados. O pré-compilador gera então um código intermediário que será lido pelo compilador.
O compilador interpreta a linguagem deste fonte intermediário e gera o código objeto, que é um código em assembler, pronto para ser utilizado.
Na fase de link-edição, o link-editor lê o código objeto gerado e identificam nele quais são as funções do sistema que foram utilizadas e busca o código das mesmas nas bibliotecas de sistema. Por fim o link-editor agrupa todos os códigos objetos e gera o programa executável final.
Diretiva #include
Sintaxe:
#include <arquivo.h>
#include “arquivo.h”
Todos os comandos para o pré-compilador começam com o caractere “#
”. O comando #include
indica para o pré-compilador ler o arquivo indicado e colocar o mesmo no programa fonte intermediário.
O arquivo incluído possui o nome de arquivo header e geralmente possui protótipo de funções a serem utilizadas por todos os programas de um sistema. Possui também as declarações de tipos existentes (typedef
) no sistema.
Quando um arquivo header pertence ao sistema (ou seja, ao compilador) deve-se colocar o nome dele entre “<
” e “>
”. Se o arquivo header for local (criado pelo programador) deve-se colocar o nome dele entre aspas. Na prática, esta regra não precisa ser seguida, pois a utilização de aspas indica ao compilador para primeiro procurar o arquivo header no diretório local e depois nos diretórios do sistema, e o <
>
indica para o compilador para primeiro procurar o arquivo header nos diretórios do sistema e depois no diretório local.
Veja os programas principal
, auxiliar
e o arquivo header soma.h
:
/* programa_principal_001.c */ /* Repare na forma como os arquivos headers foram inseridos. O arquivo header padrão do sistema tem o seu nome * preenchido entre < > e o arquivo header local tem o seu nome preenchido entre “ “. */ #include "soma.h" #include <stdio.h> int iValor_a; int iValor_b; int main (void) { int iResultado; printf ("Entre com os valores:"); scanf ("%d %d", &iValor_a, &iValor_b); imprime_soma(); /* esta funca esta declarada no arquivo soma.h */ return 0; }
/* programa_auxiliar_001.c */ #include <stdio.h> extern int iValor_a; extern int iValor_b; void imprime_soma (void) { printf ("Soma %d\n", iValor_a + iValor_b); return; }
/* soma.h, utilizado por programa_principal_001.c e programa_auxiliar_001.c */ void imprime_soma(void); /* declaracao da funcao */
Diretiva #define
Sintaxe:
#define nome_constante valor_constante
É possível definir variáveis para o pré-compilador, fazendo com que ele atribua um valor a uma variável. Sendo importante ressaltar é que esta constante é uma variável do pré-compilador e não do programa.
Cada vez que o pré-compilador encontrar esta variável, a mesma é substituída pelo conteúdo definido anteriormente, não levando em consideração o contexto de compilação.
Ressaltando que a definição de uma variável de pré-compilação é pura substituição de caracteres.
Veja o exemplo: (compile para ver a diferença: gcc programa_principal_002.c -E
)
/* programa_principal_002.c */ #include <stdio.h> #define VALOR_MAGICO 27 int main (void) { int iValor; while (1) { printf ("Entre com o valor:"); scanf ("%d", &iValor); if (iValor == VALOR_MAGICO) { break; } } return 0; }
Diretivas #if, #else e #endif
Sintaxe:
#if condição bloco de condição verdadeiro #else bloco condição falso #endif
Em certos casos é preciso selecionar um trecho de um código de acordo com uma condição pré-estabelecida, de forma que se compile ou não um trecho do código.
Esta técnica, chamada compilação condicional, é muito usada quando se tem um programa que será usado em diversas plataformas (Linux, Windows, etc) e somente um pequeno trecho de programa difere de um sistema para outro. Como é extremamente desejável que se tenha um único código, simplificando assim a manutenção e evitando riscos de alterar em um sistema e esquecer de se alterar em outro, utiliza-se à compilação condicional nos trechos diferentes.
Pode-se selecionar somente um trecho com o #if
ou selecionar entre dois trechos com o #if…#else..
O final do trecho em qualquer um dos casos é delimitado pela diretiva #endif
.
Para finalizar, ressalta-se que o #if
só será executado se na fase de pré-compilação for possível resolver a expressão condicional colocada. Portanto não é possível se fazer compilação condicional baseada em valores de variáveis da Linguagem C, pois o valor da variável só estará disponível quando o programa for executado e não durante a compilação.
A variável do teste pode ser definida internamente ou ser diretamente definida quando se chama o comando de compilação, tornando dinâmico o processo. Para se definir um valor para uma variável ao nível de comando de compilação deve-se usar a opção abaixo:
gcc progxx.c –Dvar=valor –o progxx
/* programa_ifelseendif.c */ #include <stdio.h> #define PULA 1 int main (void) { int i; for(i=1; i < 30; i++) { #if PULA == 1 if (i > 10 && i < 20) /* este if estará disponível para o compilação somente se o valor de PULA for igual a 1 */ { continue; } #endif printf ("%d\n", i); } return 0; }
Diretivas #ifdef e #ifndef
Sintaxe:
#ifdef variável_pré_definida bloco de condição verdadeiro #else bloco de condição falso #endif
ou
#ifndef variável_pré_definida bloco de condição verdadeiro #else bloco de condição falso #endif
É possível implementar a compilação condicional baseada na existência de uma variável e não em seu conteúdo. Para isto é utilizado a diretiva #ifdef
. Quando a variável especificada estiver definida, o trecho entre o #ifdef
e o #endif
será compilado, caso contrário, não.
Pode-se definir a variável no momento da compilação evitando assim a alteração de código quando da geração de versões diferentes. Usar a seguinte sintaxe para se fazer isto:
gcc progxx.c –Dvariavel –o progxx
/* programa_ifdef.c */ #include <stdio.h> #ifdef __WINDOWS__ #include <strings.h> /* manipulação de strings no windows */ #else #ifdef __LINUX__ #include <string.h> /* manipulação de strings no linux */ #else #error "Deve-se especificar __LINUX__ ou __WINDOWS__ na compilação" #endif #endif int main(void) { char sFrase[50] = "Alo mundo ", sNome[20]; printf("\nEntre com um nome:"); scanf("%s", sNome); strcat( sFrase, sNome); printf("\n%s", sFrase); return 0; }
Diretiva #error
Sintaxe:
#error mensagem
Esta diretiva deve ser usada quando se quer exigir a definição de uma ou outra variável ao nível de compilação ou internamente no programa.
/* programa_error.c */ #include <stdio.h> #ifdef __WINDOWS__ #include <strings.h> /* manipulação de strings no windows */ #else #ifdef __LINUX__ #include <string.h> /* manipulação de strings no linux */ #else #error "Deve-se especificar __LINUX__ ou __WINDOWS__ na compilação" /* Esta mensagem só aparecerá em caso de erro */ #endif #endif int main(void) { char sFrase[50] = "Alo mundo ", sNome[20]; printf("\nEntre com um nome:"); scanf("%s", sNome); strcat( sFrase, sNome); printf("\n%s", sFrase); return 0; }
Erro que ocorre no momento da compilação.
Comando utilizado: gcc programa_error.c -op
Este erro ocorreu devido a falta da diretiva no momento da compilação.
programa_error.c:10:8: error: #error "Deve-se especificar __LINUX__ ou __WINDOWS__ na compilação"
E estes erros ocorrem devido a ausência do header
correto para o compilador.
programa_error.c: In function ‘main’: programa_error.c:19: warning: incompatible implicit declaration of built-in function ‘strcat’
Diretiva #undef
Sintaxe:
#undef variável_pré_definida
Na construção de dependências pode-se ter uma situação que seja necessária desabilitar alguma variável de pré-compilação, mesmo que ela seja definida no momento da compilação.
Para isto é utilizada a diretiva #undef
, que irá retirar a definição da variável especificada.
/* programa_undef.c */ #include <stdio.h> #ifdef __WINDOWS__ #undef __LINUX__ /* previne em caso de erro no momento da compilação */ #include <strings.h> /* manipulação de strings no windows */ #endif #ifdef __LINUX__ #include <string.h> /* manipulação de strings no linux */ #endif int main(void) { char sFrase[50] = "Alo mundo ", sNome[20]; printf("\nEntre com um nome:"); scanf("%s", sNome); strcat( sFrase, sNome); printf("\n%s", sFrase); return 0; }
Variáveis pré-definidas
O pré-compilador disponibiliza uma série de variáveis de pré-compilação para serem utilizadas no programa. Essas variáveis geralmente são utilizadas para código de debug
ou log
a ser gerado por programas. São elas:
__LINE__ -> Número da linha do no arquivo fonte. __FILE__ -> Nome do arquivo fonte. __DATE__ -> Data da compilação. __TIME__ -> Hora da compilação.
#include <stdio.h> int main (void) { int i; #ifdef DEBUG /* __FILE__ sempre terá o nome do programa fonte original, mesmo que o executável printf("\nInicio do programa %s\n", __FILE__); tenha outro nome */ printf("\nVersao de %s-%s\n", __DATE__, __TIME__); /* data e hora da última compilação */ #endif for (i=1; i < 10; i++) { printf ("%d\n", i); } #ifdef DEBUG /* durante o pré-processamento, __LINE__ será substituída pela linha correspondente */ printf("\nA contagem parou! Estamos na linha %d\n", __LINE__); #endif printf("\nFim da execução\n"); #ifdef DEBUG printf("\nA última linha do programa é: %d\n", __LINE__); #endif return 0; }
Resultado da execução depois de compilar com o comando gcc programa_variaveis.c -op -DDEBUG
Versao de May 7 2009-18:22:48 1 2 3 4 5 6 7 8 9 A contagem parou! Estamos na linha 20 Fim da execução A última linha do programa é: 26
— Marcos Laureano 2009/05/07 14:31