Como o nome sugere Pré-processadores são programas que processam nosso código fonte antes da compilação. Há uma série de passos envolvidos entre escrever um programa e executar um programa em C / C+++. Vamos dar uma olhada nestes passos antes de realmente começarmos a aprender sobre Pré-processadores.
Você pode ver os passos intermediários no diagrama acima. O código fonte escrito pelos programadores é armazenado no arquivo program.c. Este arquivo é então processado pelos pré-processadores e um arquivo de código fonte expandido é gerado com o nome de programa. Este arquivo expandido é compilado pelo compilador e um arquivo de código objeto é gerado com o nome de programa .obj. Finalmente, o linker liga este arquivo de código objeto ao código objeto das funções da biblioteca para gerar o arquivo executável program.exe.
Programas pré-processadores fornecem diretivas de pré-processadores que dizem ao compilador para pré-processar o código fonte antes da compilação. Todas estas diretivas de pré-processador começam com um símbolo ‘#’ (hash). O símbolo ‘#’ indica que, qualquer declaração que comece com #, vai para o programa de pré-processamento, e o programa de pré-processamento irá executar esta declaração. Exemplos de algumas diretivas do pré-processador são: #include, #define, #ifndef etc. Lembre-se que o símbolo # fornece apenas um caminho que irá para o pré-processador, e comandos como include são processados pelo programa de pré-processamento. Por exemplo, o include incluirá código extra ao seu programa. Podemos colocar estas diretivas do pré-processador em qualquer lugar do nosso programa.
Existem 4 tipos principais de diretivas de pré-processador:
>
- Macros
- Inclusão de arquivo
- Compilação condicional
- Outras diretivas
Deixe-nos agora aprender sobre cada uma destas diretivas em detalhes.
- Macros: As macros são um pedaço de código num programa ao qual é dado algum nome. Sempre que este nome é encontrado pelo compilador, o compilador substitui o nome pelo pedaço de código real. A diretiva ‘#define’ é usada para definir uma macro. Vamos agora entender a definição da macro com a ajuda de um programa:
#include <iostream>
#define LIMIT 5
int
main()
{
for
(
int
i = 0; i < LIMIT; i++) {
std::cout << i <<
"\n"
;
}
return
0;
}
#include <stdio.h>
#define LIMIT 5
int
main()
{
for
(
int
i = 0; i < LIMIT; i++) {
printf
(
"%d \n"
,i);
}
return
0;
}
Saída:
01234
No programa acima, quando o compilador executa a palavra LIMIT ele a substitui por 5. A palavra ‘LIMIT’ na definição de macro é chamada de modelo de macro e ‘5’ é expansão de macro.
Nota: Não há nenhum ponto e vírgula(‘;’) no final da definição de macro. As definições de macro não precisam de um ponto-e-vírgula para terminar.
Macros com argumentos: Também podemos passar argumentos para as macros. Macros definidas com argumentos funcionam de forma semelhante como funções. Vamos entender isto com um programa:
#include <iostream>
#define AREA(l, b) (l * b)
int
main()
{
int
l1 = 10, l2 = 5, area;
area = AREA(l1, l2);
std::cout <<
"Area of rectangle is: "
<< area;
return
0;
}
#include <stdio.h>
#define AREA(l, b) (l * b)
int
main()
{
int
l1 = 10, l2 = 5, area;
area = AREA(l1, l2);
printf
(
"Area of rectangle is: %d"
, area);
return
0;
}
Saída:
Area of rectangle is: 50
No programa acima podemos ver que, sempre que o compilador encontra AREA(l, b) no programa ele o substitui com a declaração (l*b) . Não só isso, os valores passados para o modelo de macro AREA(l, b) também serão substituídos na instrução (l*b). Portanto, AREA(10, 5) será igual a 10*5.
- Inclusão de arquivo: Este tipo de diretiva de pré-processador diz ao compilador para incluir um arquivo no programa de código fonte. Há dois tipos de arquivos que podem ser incluídos pelo usuário no programa:
- Arquivo de cabeçalho ou arquivos padrão: Estes ficheiros contêm definição de funções pré-definidas como printf(), scanf(), etc. Estes ficheiros devem ser incluídos para trabalhar com estas funções. Diferentes funções são declaradas em diferentes ficheiros de cabeçalho. Por exemplo funções I/O standard estão em ficheiro ‘iostream’ enquanto que funções que executam operações string estão em ficheiro ‘string’.
Sintaxe:
- Arquivo de cabeçalho ou arquivos padrão: Estes ficheiros contêm definição de funções pré-definidas como printf(), scanf(), etc. Estes ficheiros devem ser incluídos para trabalhar com estas funções. Diferentes funções são declaradas em diferentes ficheiros de cabeçalho. Por exemplo funções I/O standard estão em ficheiro ‘iostream’ enquanto que funções que executam operações string estão em ficheiro ‘string’.
#include< file_name >
onde file_name é o nome do arquivo a ser incluído. Os parênteses ‘<‘ e ‘>’ dizem ao compilador para procurar o arquivo no diretório padrão.
>
- ficheiros definidos pelo utilizador: Quando um programa se torna muito grande, é uma boa prática dividi-lo em arquivos menores e incluí-los sempre que necessário. Estes tipos de ficheiros são ficheiros definidos pelo utilizador. Estes arquivos podem ser incluídos como:
#include"filename"
- Compilação Condicional: Diretrizes de Compilação Condicional são tipos de diretrizes que ajudam a compilar uma porção específica do programa ou a pular a compilação de alguma parte específica do programa com base em algumas condições. Isto pode ser feito com a ajuda de dois comandos de pré-processamento, ‘ifdef’ e ‘endif’.
Syntax:
#ifdef macro_name statement1; statement2; statement3; . . . statementN;#endif
Se a macro com nome como ‘macroname’ estiver definida então o bloco de instruções será executado normalmente, mas se não estiver definido, o compilador simplesmente pulará este bloco de instruções.
- Outras diretivas: Além das diretivas acima, existem mais duas diretivas que não são comumente usadas. Estas são:
- #undef Directive: A diretiva #undef é usada para indefinir uma macro existente. Esta diretriz funciona como:
#undef LIMIT
Utilizar esta afirmação irá indefinir a macro LIMIT existente. Após esta afirmação cada afirmação “#ifdef LIMIT” será avaliada como falsa.
- #directiva deragma: Esta diretiva é uma diretiva de propósito especial e é usada para ligar ou desligar algumas características. Este tipo de diretivas são específicas para compiladores, ou seja, elas variam de compilador para compilador. Algumas das diretivas #pragma são discutidas abaixo:
- #pragma startup e #pragma exit: Estas diretivas nos ajudam a especificar as funções que são necessárias para executar antes da inicialização do programa( antes do controle passar para main()) e pouco antes do programa sair (pouco antes do controle retornar do main()).
Note: Abaixo o programa não irá funcionar com compiladores GCC.
Localize o programa abaixo:
- #pragma startup e #pragma exit: Estas diretivas nos ajudam a especificar as funções que são necessárias para executar antes da inicialização do programa( antes do controle passar para main()) e pouco antes do programa sair (pouco antes do controle retornar do main()).
#include <bits/stdc++.h>
using
namespace
std;
void
func1();
void
func2();
#pragma startup func1
#pragma exit func2
void
func1()
{
cout <<
"Inside func1()\n"
;
}
void
func2()
{
cout <<
"Inside func2()\n"
;
}
int
main()
{
void
func1();
void
func2();
cout <<
"Inside main()\n"
;
return
0;
}
#include <stdio.h>
void
func1();
void
func2();
#pragma startup func1
#pragma exit func2
void
func1()
{
printf
(
"Inside func1()\n"
);
}
void
func2()
{
printf
(
"Inside func2()\n"
);
}
int
main()
{
void
func1();
void
func2();
printf
(
"Inside main()\n"
);
return
0;
}
Saída:
Inside func1()Inside main()Inside func2()
O código acima produzirá a saída como indicado abaixo quando executado em compiladores GCC:
Inside main()
Isso acontece porque o GCC não suporta #pragma startup ou exit. No entanto, você pode usar o código abaixo para uma saída similar em compiladores GCC.
#include <stdio.h>
void
func1();
void
func2();
void
__attribute__((constructor)) func1();
void
__attribute__((destructor)) func2();
void
func1()
{
printf
(
"Inside func1()\n"
);
}
void
func2()
{
printf
(
"Inside func2()\n"
);
}
int
main()
{
printf
(
"Inside main()\n"
);
return
0;
}
- #pragma avisa directiva: Esta directiva é usada para ocultar a mensagem de aviso que é exibida durante a compilação.
Podemos ocultar os avisos como mostrado abaixo:- #pragma warn -rvl: Esta diretiva esconde os avisos que são levantados quando uma função que é suposto retornar um valor não retorna um valor.
- #pragma warn -par: Esta diretiva esconde aqueles avisos que são levantados quando uma função que não usa os parâmetros passados para ela.
- #pragma warn -rch: Esta diretiva esconde aqueles avisos que são levantados quando um código é inalcançável. Por exemplo: qualquer código escrito após a declaração de retorno em uma função é inalcançável.
Este artigo é contribuído por Harsh Agarwal. Se você gosta de GeeksforGeeks e gostaria de contribuir, você também pode escrever um artigo usando contribute.geeksforgeeks.org ou enviar seu artigo por e-mail para [email protected]. Veja o artigo que aparece na página principal do GeeksforGeeks e ajude outros Geeks.
.