Curso de C - 12. O Pré-Processador de C

Curso de C - 12. Diretivas do Pré-Processador

O pré-processador de C é uma mini linguagem que "monta" o código-fonte para a compilação, baseada em suas diretivas. Esses comandos não fazem parte da linguagem C, mas aumentam o poder do ambiente de programação e ajudam a tornar o código mais organizado.

O padrão C ANSI define as seguintes diretivas:

  #if
  #ifdef
  #ifndef
  #else
  #elif
  #endif
  #include
  #define
  #undef
  #line
  #error
  #pragma

Em um programa, cada diretiva deve estar em sua própria linha. O comando a seguir não é válido:

  #include 	  #define MAX 20

12.1. #define

Essa diretiva já é nossa conhecida. O que ela faz é definir um identificador ou um nome de macro e uma string. Esse identificador será substituído por essa string ao ser executado o pré-processador de C (antes da compilação). Forma geral:

  #define identificador string

Como você pode ver, não há ponto-e-vírgula no final do comando. Pode-se usar vários espaços entre os comandos, e a string só será terminada por uma nova linha. Exemplo:

  #define BUFFER  1024

Toda vez que o pré-processador encontrar BUFFER ele trocará pelo valor 1024. Outro exemplo:

  #define MSG "Caractere inválido!
"

Você poderia usar o identificador MSG da seguinte forma:

  printf(MSG); 

Para strings longas, basta colocar uma no final da linha e continuar na linha seguinte:

  #define MSG "Este exemplo serve para ilustrar como usar um #define \
  com uma string longa"

Geralmente os programadores nomeiam os identificadores com letras maiúsculas, de modo a facilitar o entendimento do código. Por padrão, também, os #define são colocados no início do arquivo do código-fonte ou em um arquivo cabeçalho, visando a organização.

12.2. Definindo macros como funções

O nome da macro de um #define pode conter argumentos, de modo que você pode criar uma macro como uma função. Exemplo:

  #define QUAD(x)	(x)*(x)

Sempre que o pré-processador encontrar o nome de macro QUAD, ele o trocará pelo comando (x)*(x). Isso é uma vantagem para funções pequenas. Para funções grandes, porém, haverá um aumento do código-fonte.

12.3. #include

A diretiva #include serve para incluir um outro arquivo de código-fonte no arquivo que contém essa diretiva.

O nome do arquivo deve estar entre os símbolos de menor e maior quando o arquivo-fonte estiver em algum path de includes ou bibliotecas. Se o arquivo a incluir estiver no mesmo diretório, o nome deve estar entre aspas. Exemplo:

  #include 
  #include "funcoes.h"

12.3 Diretivas Condicionais: #if, #else, #elif, #endif

As diretivas condicionais são usadas para compilar partes específicas do código fonte, de acordo com a sua lógica. O próprio compilador "escolhe" o que deve ser compilado, de acordo com as diretivas. Veja o trecho de código abaixo:

  #include 
  
  #define TAM 100
  
  main()
  {
  	#if TAM == 100
  		printf("Igual a 100");
  	#else
  		printf("Diferente de 100");
  	#endif
  	return 0;
  }

A diretiva #if testa o valor da constante TAM. Se for igual a 100, somente o primeiro printf() é compilado. Caso contrário, o segundo printf() será compilado. Deve-se encerrar a estrutura com #endif.

Você me perguntaria: porque não usar o if e o else da linguagem C? Neste exemplo até que não seria má idéia, mas em um projeto de médias ou grandes proporções, onde valores constantes são alterados com frequência, ou existem várias versões do programa, aí sim a compilação condicional é vital. Além disso, como só é compilado o que for necessário, o código será menor e o programa consequentemente mais rápido.

Se precisar de uma cadeia de #if e #else, faça assim:

  #include 
  
  #define TAM 100
  
  main()
  {
  	#if TAM == 100
  		printf("Igual a 100");
  	#elif TAM == 150
  		printf("Igual a 150");
  	#elif TAM == 200
  		printf("Igual a 200");
  	#endif
  	return 0;
  }

O #elif é a mesma coisa que else if. Só deve existir um #endif para cada #if começado.

Pode haver aninhamento de #if. Exemplo:

  #if TAM > 100
  	#if MAX < TAM
  		n= 250;
  	#else
  		n= 200;
  	#endif
  #else
  	n= TAM
  #endif

Para cada #if começado, deve existir um #endif.

12.4. #ifdef, #ifndef, #undef e defined

Essas diretivas estendem ainda mais o poder do pré-processador de C. Elas servem para trabalhar com #defines.

A diretiva #ifdef significa "se definido", e serve para testar se uma macro foi definida anteriormente ou não. Exemplo:

  #define LINUX
  
  /* #define DOS */
  
  #ifdef LINUX
  	printf("Para Linux!");
  #else
  	printf("Para DOS!");
  #endif

Aqui temos um exemplo de como utilizar o pré-processador para gerar programas multiplataforma. Neste caso, para poder compilar para DOS, por exemplo, bastaria comentar o #define LINUX e descomentar o #define DOS.

Você deve fechar o bloco do #ifdef com #endif também.

A diretiva #ifndef significa "se não definido", e serve para testar se uma macro foi definida anteriormente. A diferença é que só será verdadeiro se a macro não tiver sido definida. Exemplo:

  #define LINUX
  
  /* #define DOS */
  
  #ifndef LINUX
  	printf("Para DOS!");
  #else
  	printf("Para Linux!");
  #endif

Como você pode ver, o primeiro printf() só será compilado se a macro LINUX não tiver sido definida (estiver comentada).

Através deste exemplo, talvez você já pode estar pensando em fazer um programa tanto para DOS como para LINUX, usando a conio.h no DOS e a ncurses no Linux... Bastaria comentar ou descomentar um pequeno trecho para a mágica acontecer... :) Viagens à parte, vamos continuar.

#undef é usado para remover a definição de uma macro anteriormente definida. Exemplo:

  #define LIN  25
  #define COL  80
  
  int tela;
  
  tela= LIN*COL;
  printf("A tela pode exibir %d caracteres", tela);
  
  #undef LIN
  #undef COL

Neste exemplo são definidos LIN e COL, que depois são usados no programa. Por último, as definições de LIN e COL são removidas com #undef.

Além de #ifdef, existe uma outra forma de verificar se uma macro foi anteriormente definida: com #if e o operador defined de tempo de compilação. Exemplo:

  #if defined LINUX
  	printf("Para Linux");
  #endif

Usar #if defined é a mesma coisa que usar #ifdef.

12.5. Macros Predefinidas, #line, #error e #pragma

Existem 5 macros predefinidas especificadas pelo padrão ANSI C:

  __LINE__
  __FILE__
  __DATE__
  __TIME__
  __STDC__

LINE: Contém o número da linha atualmente compilada. Exemplo:

  #include 

  
  main()
  {
  	printf("%d
", __LINE__); /* imprimirá 5 */
  	return 0;
  }

  __FILE__: Contém o nome do arquivo sendo compilado.

  __DATE__: Armazena a data (formato mês/dia/ano) da compilação (de código fonte para código objeto).

  __TIME__: Armazena a hora (formato hora:minuto:segundo) da compilação (de código fonte para código objeto).

  __STDC__: Se o programa seguir o padrão C ANSI, o valor de __STDC__ será 1. Caso contrário, terá qualquer outro número.

Seu compilador pode conter mais macros predefinidas. Consulte o manual.

  /* A diretiva #line muda o valor de __LINE__ e __FILE__. Sua forma geral é: */
  
  #line número "nome_arquivo"
  
  /* O número é um número inteiro que se torna o novo valor de __LINE__ e nome_arquivo 
  é uma string contendo o novo conteúdo para __FILE__. */

Exemplo:


  #include 
  
  #line 10
  main()				/* linha 10 */
  {				/* linha 11 */
  	printf("%d
", __LINE__);	/* imprime 12 */
  	return 0;
  }

A diretiva #error força o compilador a parar a compilação. Ela é mais usada para depuração. Forma geral:

  #error mensagem_erro

Quando esta diretiva é encontrada, a compilação pára e a mensagem_erro é exibida.

A diretiva #pragma pode passar várias instruções ao compilador. Você deve ler o manual do seu compilador para obter informações sobre #pragma.

12.6. Os Operadores # e ##

Estes operadores são pouco usados, mas são mantidos para serem usados em casos especiais. São usados com #define.

O operador # transforma o argumento da macro em uma string entre aspas. Exemplo:

  #include 

  
  #define ASPAS(t) # t
  
  main()
  {	
  	printf(ASPAS(Linux é o bicho));
  	return 0;
  }

O operador ## concatena dois identificadores:

  #include 
  
  #define UNE(a, b)  a ## b
  
  main()
  {
  	float fp= 0.55;
  	/* pega o f e o p e os une, formando fp */
  	printf("%f", UNE(f,p));
  	return 0;
  }

Inútil? Talvez... Quem achar utilidade para esses dois operadores, poste no fórum... :))


Powered by txt2tags



Esta notícia veio de LinuxDicas - Artigos, Dicas e Notícias Sobre o Mundo Linux
http://www.linuxdicas.com.br

O link desta notícia é:
http://www.linuxdicas.com.br/modules.php?name=Sections&op=viewarticle&artid=226