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