Ferramentas do usuário

Ferramentas do site


prog2:o_preprocessador_c

Diferenças

Aqui você vê as diferenças entre duas revisões dessa página.

Link para esta página de comparações

prog2:o_preprocessador_c [2019/01/31 17:38]
127.0.0.1 edição externa
prog2:o_preprocessador_c [2019/02/19 17:35] (atual)
maziero
Linha 1: Linha 1:
 +====== O preprocessador C ======
 +
 +A transformação de um programa C em um arquivo executável é um processo complexo, com várias etapas. Ele está ilustrado na figura a seguir:
 +
 +{{cpp.png?​600 | Do fonte ao executável}}
 +
 +  * **preprocessamento**:​ tratamento das diretivas do preprocessador (''#​include'',​ etc)
 +  * **compilação**:​ conversão de C para //​assembly//​
 +  * **tradução**:​ conversão de //​assembly//​ para código de máquina (binário)
 +  * **ligação**:​ junção dos arquivos-objeto e bibliotecas para formar o arquivo executável
 +
 +===== Preprocessamento =====
 +
 +O preprocessador C (CPP - //C PreProcessor//​) é uma ferramenta de substituição de texto invocada automaticamente pelo compilador C/C++ no início do processo de compilação. Apesar de não fazer parte formal da sintaxe da linguagem C, seu uso é praticamente indispensável para a estruturação de programas C, mesmo os mais simples.
 +
 +Todos os comandos do preprocessador começam com o símbolo ''#''​ no início de uma linha (podem haver espaços e tabs antes).
 +
 +A saída do preprocessador C, que será enviada ao compilador propriamente dito, pode ser obtida através do flags ''​-E'':​
 +
 +<​code>​
 +gcc -E *.c
 +</​code>​
 +
 +===== Inclusão de arquivos =====
 +
 +O preprocessador é muito usado para incluir arquivos externos em um código-fonte:​
 +
 +<code C>
 +#include <​stdio.h>​
 +#include "​mylib.h"​
 +
 +int main()
 +{
 +  printf ("​Hello,​ world\n"​) ;
 +  return (0) ;
 +}
 +</​code>​
 +
 +No exemplo acima, o preprocessador irá **substituir** cada linha ''#​include ...''​ pelo conteúdo do respectivo arquivo, gerando um único arquivo temporário que será entregue ao compilador C.
 +
 +Há uma diferença importante entre as duas formas de inclusão:
 +
 +  * ''#​include <​...>'':​ o arquivo indicado será buscado nos diretórios default do compilador, geralmente ''/​usr/​include/​*''​ nos sistemas Unix.
 +  * ''#​include "​..."'':​ o arquivo indicado será buscado primeiro no diretório corrente (onde está o arquivo que está sendo compilado), e depois nos diretórios default do compilador.
 +
 +===== Definição e uso de constantes =====
 +
 +O preprocessador é frequentemente usado para a definição de constantes, através do comando ''#​define'':​
 +
 +<code C>
 +#define VETSIZE 64
 +
 +int vetor[VETSIZE] ;
 +
 +for (i=0; i<​VETSIZE;​ i++)
 +  vetor[i] = 0 ;
 +</​code>​
 +
 +Após a definição,​ todas as ocorrências da string ''​VETSIZE''​ no arquivo serão substituídas pelo valor 64, **antes da compilação**,​ resultando no seguinte código-fonte:​
 +
 +<code C>
 +int vetor[64] ;
 +
 +for (i=0; i<64; i++)
 +  vetor[i] = 0 ;
 +</​code>​
 +
 +<note important>​
 +Para evitar confusões entre variáveis da linguagem C e constantes do preprocessador,​ convencionou-se definir as constantes em MAIÚSCULAS.
 +</​note>​
 +
 +===== Constantes predefinidas =====
 +
 +Algumas constantes são definidas previamente pelo sistema:
 +
 +  * ''​%%__DATE__%%'':​ data atual (formato "MMM DD YYYY")
 +  * ''​%%__TIME__%%'':​ horário atual (formato "​HH:​MM:​SS"​)
 +  * ''​%%__FILE__%%'':​ nome do arquivo corrente.
 +  * ''​%%__LINE__%%'':​ número da linha corrente do código-fonte.
 +
 +Além destas, muitas outras constantes podem estar disponíveis,​ dependendo da plataforma:
 +
 +  * [[https://​gcc.gnu.org/​onlinedocs/​cpp/​Predefined-Macros.html|Macros predefinidas no preprocessador GNU]]
 +  * [[https://​sourceforge.net/​p/​predef/​wiki/​Home/​|Pre-defined C/C++ Compiler Macros]]
 +
 +===== Compilação condicional =====
 +
 +Uma constante pode ser definida sem ter um valor específico,​ funcionando como um //flag// que pode ser testado pelo preprocessador através de comandos específicos:​
 +
 +<code C>
 +#define DEBUG
 +
 +...
 +
 +#ifdef DEBUG
 +  printf ("​Valor de i: %d\n", i) ;
 +#endif
 +</​code>​
 +
 +No exemplo acima, a linha com o ''​printf''​ só estará presente no código enviado ao compilador se a constante ''​DEBUG''​ estiver definida. ​
 +
 +Deve-se observar que constantes podem ser definidas no código-fonte usando o comando ''#​define''​ (como nos exemplos acima), mas também podem ser definidas na linha de comando, ao invocar o compilador:
 +
 +<​code>​
 +gcc -DVETSIZE=64 arquivo1.c
 +gcc -DDEBUG arquivo2.c
 +</​code>​
 +
 +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
 +</​code>​
 +
 +Da mesma forma, pode-se evitar redefinir constantes que já estejam definidas:
 +
 +<code c>
 +#ifndef NULL
 +#define NULL (void *) 0
 +#endif
 +</​code>​
 +
 +Além do ''​ifdef'',​ existem outros operadores condicionais,​ como o ''​if''​ - ''​elif''​ - ''​else'':​
 +
 +<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
 +</​code>​
 +
 +O operador ''​defined''​ permite testar se uma macro está definida:
 +
 +<code c>
 +#if defined (__arm__) ​        // macro definida em sistemas com ARM
 +  #warning "​Generating code for ARM processor."​
 +  // code for ARM processors
 +  ...
 +#elif defined (__i386__) ​     // idem, x86
 +  #warning "​Generating code for x86 processor."​
 +  // code for Intel 32-bit processors
 +  ...
 +#else
 +  // abort compilation
 +  #error "​Unknown architecture,​ aborting."​
 +#endif
 +</​code>​
 +
 +<note tip>
 +Observe o uso das diretivas ''#​warning''​ e ''#​error''​ no programa acima.
 +</​note>​
 +
 +===== Macros com parâmetros =====
 +
 +O preprocessador é usado com frequência para construir macros, que são funções simples com parâmetros:​
 +
 +<code c>
 +#define SQUARE(x) x*x
 +...
 +printf ("O quadrado de 5 é %d\n", SQUARE(5)) ;
 +</​code>​
 +
 +O código acima, ao ser tratado pelo preprocessador,​ será transformado em:
 +
 +<code c>
 +printf ("O quadrado de 5 é %d\n", 5*5) ;
 +</​code>​
 +
 +Observe que a macro ''​SQUARE''​ não computou o resultado de 5*5, apenas fez a substituição do parâmetro 5 pela expressão que ela define (5*5).
 +
 +Deve-se tomar muito cuidado ao definir macros com parâmetros,​ pois eles podem ser avaliados de forma errada pelo compilador. O código abaixo apresenta um erro dessa natureza:
 +
 +<code c>
 +#define SQUARE(x) x*x
 +...
 +printf ("O quadrado de 2+3 é %d\n", SQUARE(2+3)) ;
 +</​code> ​
 +
 +Após o preprocessador teremos:
 +
 +<code c>
 +printf ("O quadrado de 2+3 é %d\n", 2+3*2+3) ;
 +</​code>​
 +
 +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. Para evitar esse erro, a macro deve ser declarada usando **parênteses**:​
 +
 +<code c>
 +#define SQUARE(x) (x)*(x)
 +...
 +printf ("O quadrado de 2+3 é %d\n", SQUARE(2+3)) ;
 +</​code> ​
 +
 +Que resulta em:
 +
 +<code c>
 +printf ("O quadrado de 2+3 é %d\n", (2+3)*(2+3)) ;
 +</​code>​
 +
 +===== 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://​gcc.gnu.org/​onlinedocs/​cpp/​index.html|The C Preprocessor]].
  
prog2/o_preprocessador_c.txt · Última modificação: 2019/02/19 17:35 por maziero