Jak sama nazwa wskazuje Preprocesory są programami, które przetwarzają nasz kod źródłowy przed kompilacją. Istnieje wiele kroków pomiędzy napisaniem programu a wykonaniem programu w C / C++. Rzućmy okiem na te kroki, zanim faktycznie zaczniemy uczyć się o Preprocesorach.

Możesz zobaczyć pośrednie kroki na powyższym diagramie. Kod źródłowy napisany przez programistów jest przechowywany w pliku program.c. Plik ten jest następnie przetwarzany przez preprocesory i generowany jest rozszerzony plik z kodem źródłowym o nazwie program. Ten rozszerzony plik jest kompilowany przez kompilator i generowany jest plik z kodem obiektowym o nazwie program .obj. Na koniec linker łączy ten plik kodu obiektowego z kodem obiektowym funkcji bibliotecznych, aby wygenerować plik wykonywalny program.exe.

Procesory programy dostarczają dyrektywy preprocesorów, które mówią kompilatorowi, aby wstępnie przetworzyć kod źródłowy przed kompilacją. Wszystkie te dyrektywy preprocesora zaczynają się od symbolu '#’ (hash). Symbol '#’ wskazuje, że jakakolwiek instrukcja zaczynająca się od #, jest kierowana do programu preprocesora, a program preprocesora wykona tę instrukcję. Przykładami niektórych dyrektyw preprocesora są: #include, #define, #ifndef itd. Pamiętaj, że # symbol podaje tylko ścieżkę, którą ma podążać do preprocesora, a polecenie takie jak include jest przetwarzane przez program preprocesora. Na przykład, include spowoduje dołączenie dodatkowego kodu do naszego programu. Możemy umieścić te dyrektywy preprocesora w dowolnym miejscu w naszym programie.

Istnieją 4 główne typy dyrektyw preprocesora:

  1. Makra
  2. Włączanie plików
  3. Kompilacja warunkowa
  4. Inne dyrektywy

Poznajmy teraz szczegółowo każdą z tych dyrektyw.

  • Makra: Makra to fragment kodu w programie, któremu nadano pewną nazwę. Kiedykolwiek kompilator napotka tę nazwę, zastępuje ją faktycznym fragmentem kodu. Dyrektywa '#define’ jest używana do definiowania makra. Zrozummy teraz definicję makra z pomocą programu:
C++

#include <iostream>
#define LIMIT 5
int main()
{
for (int i = 0; i < LIMIT; i++) {
std::cout << i << "\n";
}
return 0;

C

#include <stdio.h>
#define LIMIT 5
int main()
{
for (int i = 0; i < LIMIT; i++) {
printf("%d \n",i);
}
return 0;
}



Wyjście:

01234

W powyższym programie, gdy kompilator wykonuje słowo LIMIT zastępuje je liczbą 5. Słowo 'LIMIT’ w definicji makra nazywamy szablonem makra, a '5′ to rozwinięcie makra.

Uwaga: Na końcu makrodefinicji nie ma średnika(’;’). Definicje makr nie potrzebują średnika na końcu.

Makra z argumentami: Do makr możemy również przekazywać argumenty. Makra zdefiniowane z argumentami działają podobnie jak funkcje. Zrozummy to na przykładzie programu:

C++

#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;
}



C

#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;
}



Wyjście:

Area of rectangle is: 50

Z powyższego programu widzimy, że ilekroć kompilator znajdzie w programie AREA(l, b) zastępuje go stwierdzeniem (l*b) . Mało tego, wartości przekazane do makrowzorca AREA(l, b) również zostaną zastąpione instrukcją (l*b). Dlatego AREA(10, 5) będzie równe 10*5.

  • Włączanie plików: Ten typ dyrektywy preprocesora mówi kompilatorowi, aby włączył plik do programu z kodem źródłowym. Istnieją dwa rodzaje plików, które mogą być dołączone przez użytkownika do programu:
    • Plik nagłówkowy lub pliki standardowe: Pliki te zawierają definicje predefiniowanych funkcji takich jak printf(), scanf() itp. Te pliki muszą być dołączone, aby móc pracować z tymi funkcjami. Różne funkcje są zadeklarowane w różnych plikach nagłówkowych. Na przykład standardowe funkcje I/O są w pliku 'iostream’, podczas gdy funkcje, które wykonują operacje na łańcuchach są w pliku 'string’.
      Syntaktyka:
#include< file_name >

gdzie nazwa_pliku jest nazwą pliku, który ma być dołączony. Nawiasy '<’ i ’>’ mówią kompilatorowi, aby szukał pliku w standardowym katalogu.

  • pliki zdefiniowane przez użytkownika: Kiedy program staje się bardzo duży, dobrą praktyką jest podzielenie go na mniejsze pliki i dołączanie, kiedy tylko jest to potrzebne. Tego typu pliki są plikami zdefiniowanymi przez użytkownika. Pliki te mogą być dołączone jako:
#include"filename"
  • Kompilacja warunkowa: Dyrektywy kompilacji warunkowej są typem dyrektyw, które pomagają skompilować określoną część programu lub pominąć kompilację określonej części programu w oparciu o pewne warunki. Można to zrobić za pomocą dwóch poleceń preprocessingu 'ifdef’ i 'endif’.
    Syntaktyka:
#ifdef macro_name statement1; statement2; statement3; . . . statementN;#endif

Jeśli makro o nazwie takiej jak 'macroname’ jest zdefiniowane, to ten blok instrukcji wykona się normalnie, ale jeśli nie jest zdefiniowane, to kompilator po prostu pominie ten blok instrukcji.

  • Inne dyrektywy: Poza powyższymi dyrektywami istnieją jeszcze dwie dyrektywy, które nie są powszechnie używane. Są to:
    • Dyrektywa #undef: Dyrektywa #undef jest używana do niezdefiniowania istniejącego makra. Dyrektywa ta działa jak:
#undef LIMIT

Użycie tej instrukcji spowoduje niezdefiniowanie istniejącego makra LIMIT. Po tej instrukcji każda instrukcja „#ifdef LIMIT” będzie miała wartość false.

  • Dyrektywa #pragma: Ta dyrektywa jest dyrektywą specjalnego przeznaczenia i jest używana do włączania lub wyłączania pewnych funkcji. Dyrektywy tego typu są specyficzne dla kompilatora, to znaczy, różnią się w zależności od kompilatora. Niektóre z dyrektyw #pragma są omówione poniżej:
    • #pragma startup i #pragma exit: Te dyrektywy pomagają nam określić funkcje, które są potrzebne do uruchomienia przed startem programu( zanim sterowanie przejdzie do main()) i tuż przed wyjściem z programu (tuż przed powrotem sterowania z main()).
      Uwaga: Poniższy program nie będzie działał z kompilatorami GCC.
      Przyjrzyj się poniższemu programowi:
C++

#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;
}



C

#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;
}



Wydawnictwo:

Inside func1()Inside main()Inside func2()

Powyższy kod wytworzy dane wyjściowe jak poniżej, gdy zostanie uruchomiony na kompilatorach GCC:

Inside main()

Dzieje się tak, ponieważ GCC nie obsługuje #pragma startup lub exit. Możesz jednak użyć poniższego kodu, aby uzyskać podobne dane wyjściowe na kompilatorach GCC.

C

#include <stdio.h>
void func1();
void func2();
void __attribute__((constructor)) func1();
void __attribute__((destructor)) func2();
void func1()
{
printf("Inside func1()\n");
}

return 0;
}



  • #pragma warn Dyrektywa: Dyrektywa ta służy do ukrywania komunikatów ostrzegawczych, które są wyświetlane podczas kompilacji.
    Możemy ukryć ostrzeżenia jak pokazano poniżej:
    • #pragma warn -rvl: Ta dyrektywa ukrywa te ostrzeżenia, które są podnoszone, gdy funkcja, która ma zwrócić wartość, nie zwraca wartości.
    • #pragma warn -par: Ta dyrektywa ukrywa te ostrzeżenia, które są podnoszone, gdy funkcja nie używa przekazanych jej parametrów.
    • #pragma warn -rch: Ta dyrektywa ukrywa te ostrzeżenia, które są podnoszone, gdy kod jest nieosiągalny. Na przykład: każdy kod napisany po instrukcji return w funkcji jest nieosiągalny.

Ten artykuł został napisany przez Harsh Agarwal. Jeśli podoba Ci się GeeksforGeeks i chciałbyś przyczynić się do jego powstania, możesz również napisać artykuł korzystając z adresu contribute.geeksforgeeks.org lub wysłać go pocztą na adres [email protected]. Zobacz, jak twój artykuł pojawia się na stronie głównej GeeksforGeeks i pomóż innym Geekom.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.