Pré-processador C – Parte 1

Pré-processador C

Olá, caro leitor! Este artigo tem o intuito de expor, de maneira introdutória, alguns recursos do pré-processador C. Desse modo, o artigo foi estruturado para apresentar alguns exemplos básicos e algumas aplicações para você, leitor, entender melhor o assunto. Vamos lá!

Afinal, o que é o Pré-processador C?

Durante o processo de compilação de um programa você pode instruir o compilador sobre como realizar determinada tarefa. Uma das formas de fazer isso no próprio código-fonte é utilizando os recursos do pré-processador C. Especificamente, o pré-processador C é uma ferramenta utilizada antes do estágio de compilação para realizar uma transformação textual no código-fonte.

O Pré-processador C

As instruções dadas para o compilador são definidas a partir das diretivas do pré-processador. O padrão C ANSI define as seguintes diretivas:

Essas diretivas possibilitam instruir o compilador a realizar as seguintes tarefas:

  • Incluir arquivos de cabeçalho (.h – header);
  • Compilação condicional;
  • Definir constantes e/ou macros;
  • Expansão de macros;
  • Controle e diagnóstico do processo de compilação.

Cabe frisar que essas diretivas não fazem parte da linguagem C, contudo fazem parte de uma ferramenta muito útil para o desenvolvimento de programas em C.

Formato das diretivas do Pré-processador C

Convém observar que todas as diretivas do pré-processador C são iniciadas com o símbolo ‘#’ seguida do nome da diretiva e devem, obrigatoriamente, ser declaradas em linhas diferentes.

Como regra para utilização das diretivas temos que:

  • O símbolo # deve ser o primeiro caractere (exceto o espaço em branco) da linha;
  • Toda diretiva é terminada ao final de cada linha, a não ser que a linha seja finalizada pelo símbolo backslash “\”;
  • Com exceção da diretiva #pragma todas as diretivas podem ser utilizadas em qualquer parte do programa.

Incluindo arquivos – #include

Modularizar e reutilizar códigos são práticas comuns e de grande importância. Para isso é necessário instruir o compilador a incluir arquivos-fonte no processo de compilação. No pré-processador C essa tarefa é realizada pela diretiva #include.

A diretiva #include instrui o compilador a ler e compilar um arquivo-fonte. De forma geral o #include determina o nome do arquivo que será processado. Por exemplo, considere o famoso Hello World: 

Ao processar o arquivo, a diretiva #include instrui o compilador a ler e compilar o arquivo stdio.h. Mas afinal, onde está localizado o arquivo stdio.h?

Para responder essa pergunta é necessário antes verificar a sintaxe da diretiva. Basicamente existem duas maneiras de incluir um arquivo. Uma é utilizando os símbolos ‘<’ e ‘>’, já a outra é utilizando aspas. Veja: 

Ao utilizar os símbolos de maior e menor você instrui o compilador a procurar o arquivo nos diretórios do sistema, neste caso, definido pelo compilador. Já na outra abordagem, o arquivo é procurado no diretório do código-fonte (diretório de trabalho).

Definindo macros – #define

A diretiva #define é utilizada para definir um nome (identificador, denominado macro) que mantém um valor associado. No estágio inicial da compilação o identificador é substituído pelo seu valor.

Considere o exemplo básico para piscar um LED no Arduino:

No código de exemplo, o valor 13 refere-se ao pino de saída que aciona um LED. É possível observar também que esse valor é repetido em outras partes do programa.

Uma aplicação prática do #define é para reduzir a repetição de valores e assim evitar possíveis erros também. Neste caso é válido criar um #define que possui o valor 13, como abaixo:

Ao processar o arquivo o identificador LED_PIN é substituído pelo seu valor, neste caso, o valor 13. Desse modo se o projeto é alterado de forma que o LED seja acionado por outro pino, bastaria alterar o valor da macro.

Observe também que é possível expandir macros em vários níveis como no caso da definição:

Nesse caso, LED_ON tem como valor a macro HIGH que, por sua vez, é definida como na biblioteca Arduino.h:

As macros não ficam limitadas apenas em relacionar nomes e valores. A partir das macros também é possível definir trechos de código e até mesmo receber parâmetros.

Definindo macros com parâmetros – #define

Para explicar esse tópico vou utilizar o exemplo da rotina MAX. A rotina MAX é utilizada para definir o maior valor entre dois valores informados:

ou ainda, no modo if-inline: 

O trecho de código pode ser convertido em uma macro que recebe os dois valores:

E sua aplicação no código fica assim: 

Por fazer simplesmente a substituição da macro pelo seu valor, alguns cuidados devem ser tomados. Convém observar que os parâmetros devem sempre ser utilizados entre parênteses. Considere que MAX tivesse sido definido como:

e:

Durante a reposição da macro pelo seu valor, o código gerado seria:

Seria gerado o valor errado!

Definindo macros com várias linhas – #define

Para definir uma macro em mais de uma linha o símbolo ‘\’ (backslash) deve ser utilizado no final da linha. Esse símbolo índica que a definição da macro continua na próxima linha. 

Geralmente as macros com mais de uma instrução são criadas como blocos de código estruturados por um do {} while (0). Considere o exemplo abaixo. 

O processo de compilação seria encerrado com a seguinte mensagem:

Isso ocorre porque a substituição da macro transforma o código sem utilizar os delimitadores de bloco {}. 

Assim uma macro que utiliza o formato do {} while (0) compilaria corretamente. 

Removendo definições – #undef

Para remover uma definição é necessário utilizar a diretiva #undef. A remoção torna-se necessária para que seja possível definir uma outra macro com o mesmo identificador. 

Operadores # e ##

O pré-processador C ainda inclui recursos para converter parâmetros da macro em uma string constante ou ainda realizar a concatenação de tokens.

O processo de conversão para uma string constante é chamado de Stringication e é realizado dentro de uma macro a partir do símbolo cerquilha (#). No exemplo abaixo a macro MSG_FORMAT converte o parâmetro type em uma string. 

Já a concatenação é realizada a partir da sequência ##. No exemplo abaixo a macro MSG_TYPE é utilizada para definir os elementos de uma enumeração. O parâmetro type é concatenado no final do texto _MSG

Conclusão

Neste artigo foi apresentado de maneira introdutória alguns recursos do pré-processador C. Como se pode ver o pré-processador expande os recursos do ambiente de programação tornando-se uma ferramenta essencial para o desenvolvimento de programas em linguagem C.

Na segunda parte deste artigo serão apresentados os recursos do pré-processador que possibilitam realizar a compilação condicional de trechos de código.

Saiba mais: Série “Pré-processador C”

– Pré-processador C – Parte 1

Pré-processador C: Compilação condicional – Parte 2

– Pré-processador C: Diagnóstico – Parte 3

– Pré-processador C: X macros – Parte 4

Referências

Livro: C, completo e total – 3ª edição revista e atualizada. Herbert Schildt.

https://www.arduino.cc/en/Reference/DigitalWrite

Fonte da imagem destacada: http://listamaze.com/top-10-programming-languages-for-job-security/

Outros artigos da série

Pré-processador C: Compilação condicional – Parte 2 >>
Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.

Receba os melhores conteúdos sobre sistemas eletrônicos embarcados, dicas, tutoriais e promoções.

Software » Pré-processador C – Parte 1
Comentários:
Notificações
Notificar
guest
14 Comentários
recentes
antigos mais votados
Inline Feedbacks
View all comments
Rafael Dias
Rafael Dias
01/09/2015 13:04

muito bom!

Só tem um pequeno erro no começo:

“Toda diretiva é terminada ao final de cada linha, a não ser que a linha seja finalizada pelo símbolo backslash (/);”
o backslach é o “”.

Tanto que você o utiliza no decorrer do texto.

Fernando Deluno Garcia
Fernando Deluno Garcia
Reply to  Rafael Dias
01/09/2015 13:48

Olá, Rafael.

obrigado pela correção!

Leandro Poloni Dantas
31/08/2015 13:56

Fernando,
Muito bom seu artigo. Vou recomendá-lo para meus alunos.

Fernando Deluno Garcia
Fernando Deluno Garcia
Reply to  Leandro Poloni Dantas
01/09/2015 09:17

Olá, Leandro.

Fico feliz que tenha gostado. Espero que seja proveitoso para os seus alunos!

Obrigado pelo retorno!

Rafael Alves Dias
Rafael
31/08/2015 12:34

Muito bom o artigo Fernando.
São detalhes que fazem a diferença…

Sempre tive dificuldade em quando usar certas coisas, mas com esse artigo começa a clarear a aplicação de cada coisa…

Agradeço por compartilhar seu conhecimento, no aguardo do próximo

Fernando Deluno Garcia
Fernando Deluno Garcia
Reply to  Rafael
01/09/2015 09:13

Olá, Rafael.

Realmente os detalhes fazem a diferença. Muitas vezes apenas utilizamos algo mas não sabemos o significado. Nos próximos artigos vou mostrar alguns recursos que podem ajudar bastante no desenvolvimento de aplicações.

Obrigado pelo retorno!

Marcos Lourival Magalhães
Marcos
31/08/2015 11:40

Simples e objetivo, muito bom

Fernando Deluno Garcia
Fernando Deluno Garcia
Reply to  Marcos
01/09/2015 09:08

Obrigado, Marcos!

Haroldo Amaral
Haroldo Amaral
27/08/2015 15:18

Excelente artigo Fernando.
Aguardando os próximos!

Fernando Deluno Garcia
Fernando Deluno Garcia
Reply to  Haroldo Amaral
28/08/2015 10:01

Olá, Haroldo.

Muito obrigado pelo retorno!

Lenio Rodrigues
26/08/2015 13:39

Ótimo conteúdo caro Fernando ! no aguardo da segunda parte ! muito grato !

Fernando Deluno Garcia
Fernando Deluno Garcia
Reply to  Lenio Rodrigues
26/08/2015 16:48

Olá, Lenio.
Obrigado pelo retorno!

Fico feliz que tenha gostado.

Cesar Junior
Cesar Junior
26/08/2015 08:45

Parabéns pelo texto. No aguardo das outras partes.

Fernando Deluno Garcia
Fernando Deluno Garcia
Reply to  Cesar Junior
26/08/2015 16:46

Obrigado, Cesar.
Espero que goste dos próximos!

Talvez você goste:

Séries



Outros da Série

Menu