Diferenças
Aqui você vê as diferenças entre duas revisões dessa página.
Ambos lados da revisão anterior Revisão anterior | |||
c:o_preprocessador_c [2023/08/15 14:53] – [Definição e uso de constantes] maziero | c:o_preprocessador_c [2023/08/15 14:53] (atual) – [Compilação condicional] maziero | ||
---|---|---|---|
Linha 1: | Linha 1: | ||
+ | ====== O preprocessador C ====== | ||
+ | |||
+ | {{ progc_preprocessor.mkv |Video desta aula}} | ||
+ | |||
+ | A transformação de um programa C em um arquivo executável é um processo complexo, com várias etapas, sendo as mais importantes: | ||
+ | |||
+ | * **preprocessamento**: | ||
+ | * **compilação**: | ||
+ | * **tradução**: | ||
+ | * **ligação**: | ||
+ | |||
+ | Ele está ilustrado na figura a seguir, onde '' | ||
+ | |||
+ | {{ cpp.png? | ||
+ | |||
+ | ===== Preprocessamento ===== | ||
+ | |||
+ | O preprocessador C (CPP - //C PreProcessor// | ||
+ | |||
+ | Todos os comandos do preprocessador começam com o símbolo ''#'' | ||
+ | |||
+ | A saída do preprocessador C, que será enviada ao compilador propriamente dito, pode ser obtida através do flags '' | ||
+ | |||
+ | < | ||
+ | gcc -E arquivo.c | ||
+ | </ | ||
+ | |||
+ | ===== Inclusão de arquivos ===== | ||
+ | |||
+ | O preprocessador é frequentemente usado para incluir arquivos externos em um código-fonte. | ||
+ | |||
+ | Considere este exemplo: | ||
+ | |||
+ | <code c escreva.h> | ||
+ | #ifndef __ESCREVA__ | ||
+ | #define __ESCREVA__ | ||
+ | |||
+ | void escreva (char *msg) ; | ||
+ | |||
+ | #endif | ||
+ | </ | ||
+ | |||
+ | <code c escreva.c> | ||
+ | #include < | ||
+ | |||
+ | void escreva (char *msg) | ||
+ | { | ||
+ | printf (" | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | <code c hello.c> | ||
+ | #include " | ||
+ | |||
+ | int main () | ||
+ | { | ||
+ | escreva (" | ||
+ | return (0) ; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Neste exemplo, o preprocessador irá **substituir** cada linha ''# | ||
+ | |||
+ | Há uma diferença importante entre as duas formas de inclusão: | ||
+ | |||
+ | * ''# | ||
+ | * ''# | ||
+ | |||
+ | ===== Definição e uso de constantes ===== | ||
+ | |||
+ | O preprocessador é frequentemente usado para a definição de constantes, através do comando ''# | ||
+ | |||
+ | <code c vetor.c> | ||
+ | #define VETSIZE 64 | ||
+ | |||
+ | int main () | ||
+ | { | ||
+ | int vetor[VETSIZE] ; | ||
+ | |||
+ | for (i = 0; i < VETSIZE; i++) | ||
+ | vetor[i] = i ; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Após a definição, | ||
+ | |||
+ | <code C> | ||
+ | int vetor[64] ; | ||
+ | |||
+ | for (i = 0; i < 64; i++) | ||
+ | vetor[i] = 0 ; | ||
+ | </ | ||
+ | |||
+ | <note important> | ||
+ | Para evitar confusões entre variáveis da linguagem C e constantes do preprocessador, | ||
+ | </ | ||
+ | |||
+ | ===== Constantes predefinidas ===== | ||
+ | |||
+ | Algumas constantes são definidas previamente pelo sistema: | ||
+ | |||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | |||
+ | Um exemplo de uso: | ||
+ | |||
+ | <code c data.c> | ||
+ | #include < | ||
+ | |||
+ | int main () | ||
+ | { | ||
+ | printf ("Este código foi compilado em %s\n", __DATE__) ; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Além destas, muitas outras constantes podem estar disponíveis, | ||
+ | |||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | |||
+ | ===== Compilação condicional ===== | ||
+ | |||
+ | Uma constante pode ser definida sem um valor específico, | ||
+ | |||
+ | <code C debug.c> | ||
+ | #include < | ||
+ | |||
+ | #define VETSIZE 64 | ||
+ | |||
+ | int main () | ||
+ | { | ||
+ | int i, vetor[VETSIZE] ; | ||
+ | |||
+ | for (i = 0; i < VETSIZE; i++) | ||
+ | { | ||
+ | vetor[i] = i ; | ||
+ | #ifdef DEBUG | ||
+ | printf (" | ||
+ | #endif | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | No exemplo acima, a linha do '' | ||
+ | |||
+ | Constantes podem ser definidas no código-fonte usando o comando ''# | ||
+ | |||
+ | < | ||
+ | gcc -DDEBUG debug.c | ||
+ | </ | ||
+ | |||
+ | Um uso frequente da compilação condicional é a construção de //include guards//, ou seja código para evitar múltiplas inclusões do mesmo arquivo: | ||
+ | |||
+ | <code c headers.h> | ||
+ | #ifndef _THIS_HEADER_FILE_ | ||
+ | #define _THIS_HEADER_FILE_ | ||
+ | ... | ||
+ | #endif | ||
+ | </ | ||
+ | |||
+ | Da mesma forma, pode-se evitar redefinir constantes que já estejam definidas: | ||
+ | |||
+ | <code c> | ||
+ | #ifndef NULL | ||
+ | #define NULL (void *) 0 | ||
+ | #endif | ||
+ | </ | ||
+ | |||
+ | Além do '' | ||
+ | |||
+ | <code c> | ||
+ | #if DEBUG_LEVEL > 5 | ||
+ | // print all debug messages | ||
+ | ... | ||
+ | #elif DEBUG_LEVEL > 3 | ||
+ | // print relevant debug messages | ||
+ | ... | ||
+ | #elif DEBUG_LEVEL > 1 | ||
+ | // print prioritary debug messages | ||
+ | ... | ||
+ | #else | ||
+ | // print no debug messages | ||
+ | #endif | ||
+ | </ | ||
+ | |||
+ | O operador '' | ||
+ | |||
+ | <code c> | ||
+ | #if defined (__arm__) | ||
+ | #warning " | ||
+ | // code for ARM processors | ||
+ | ... | ||
+ | #elif defined (__i386__) | ||
+ | #warning " | ||
+ | // code for Intel 32-bit processors | ||
+ | ... | ||
+ | #else | ||
+ | // abort compilation | ||
+ | #error " | ||
+ | #endif | ||
+ | </ | ||
+ | |||
+ | <note tip> | ||
+ | Observe o uso das diretivas ''# | ||
+ | </ | ||
+ | |||
+ | ===== Macros com parâmetros ===== | ||
+ | |||
+ | O preprocessador é usado com frequência para construir macros, que são funções simples com parâmetros: | ||
+ | |||
+ | <code c macro1.c> | ||
+ | #include < | ||
+ | |||
+ | #define SQUARE(x) x*x | ||
+ | |||
+ | int main () | ||
+ | { | ||
+ | printf ("O quadrado de 5 é %d\n", SQUARE(5)) ; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | O código acima, ao ser tratado pelo preprocessador, | ||
+ | |||
+ | <code c> | ||
+ | printf ("O quadrado de 5 é %d\n", 5*5) ; | ||
+ | </ | ||
+ | |||
+ | Observe que a macro '' | ||
+ | |||
+ | <note important> | ||
+ | Tome **muito** cuidado ao definir macros com parâmetros, | ||
+ | </ | ||
+ | |||
+ | O exemplo abaixo apresenta um erro dessa natureza: | ||
+ | |||
+ | <code c> | ||
+ | #define SQUARE(x) x*x | ||
+ | ... | ||
+ | printf ("O quadrado de 2+3 é %d\n", SQUARE(2+3)) ; | ||
+ | </ | ||
+ | |||
+ | O preprocessador transformará essa expressão em: | ||
+ | |||
+ | <code c> | ||
+ | printf ("O quadrado de 2+3 é %d\n", 2+3*2+3) ; | ||
+ | </ | ||
+ | |||
+ | O resultado da expressão deveria ser 25 (o quadrado de 2+3) mas será 11, por causa da precedência entre os operadores aritméticos (que o preprocessador não trata). | ||
+ | |||
+ | Para evitar esse erro, a macro deve ser declarada usando **parênteses**: | ||
+ | |||
+ | <code c macro2.c> | ||
+ | #include < | ||
+ | |||
+ | #define SQUARE(x) (x)*(x) | ||
+ | |||
+ | int main () | ||
+ | { | ||
+ | printf ("O quadrado de 5 é %d\n", SQUARE(2+3)) ; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Que resulta na expressão correta: | ||
+ | |||
+ | <code c> | ||
+ | printf ("O quadrado de 2+3 é %d\n", (2+3)*(2+3)) ; | ||
+ | </ | ||
+ | |||
+ | ===== Operações avançadas ===== | ||
+ | |||
+ | O preprocessador C tem operações avançadas que vão muito além do escopo desta breve introdução. Para uma visão mais completa e profunda consulte este documento: [[https:// | ||