Pré-Compilação

Fases de uma compilação

Fases de uma compilação

O processo de compilação de um programa é constituído de três fases distintas:

  1. pré-compilação;
  2. compilação;
  3. 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

pre_compilacao.txt · Última modificação: 2009/05/07 14:22 por laureano
CC Attribution-Noncommercial-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0