menu

darmowe eBooki

Efekt Motyla

okładka

Książka, która wywróci Twoje życie do góry nogami

Sprawdź sam, czytając darmowy fragment eBooka Efekt Motyla.

Czy ta wiedza okazała się dla Ciebie przydatna? Chcesz wiedzieć więcej? Zobacz tutaj.

Google Adsense

migawka z forum

Dowcipy o programistach/informatykach

Witam, grunt to potrafić śmiać sie z samego siebie  ;DPrzychodzi informatyk do domu po pra...

Rysiek z "Klanu"

W związku z ogromnym poruszeniem we wszystkich mediach w naszym kraju, dotyczącym tak kultowej postaci jak Rysiek z &quo...

Propozycje zmian w serwisie Guide C++

Witam, ze względu na to że nie znalazłem podobnego tematu na forum, a mam kilka sugestii   ;) postanowiłem je ...

buttony

SDJ
vortal programistów
Vademecum sieci komputerowych
Soldiers'04 - fan klub Legii Warszawa
www.katalog.bajery.pl
katalog stron
katalog najlepszych stron
jestem w katalogu
Wszystko o C++
[zamknij]

Korzystamy z plików cookies i umożliwiamy zamieszczanie ich osobom trzecim. Pliki cookie pozwalają na poznanie twoich preferencji na podstawie zachowań w serwisie.
Uznajemy, że jeżeli kontynuujesz korzystanie z serwisu, wyrażasz na to zgodę.

jesteś w: Kurs C++ / Dyrektywy preprocesora
Szkoła Hakerów - Kurs Hackingu Bez Cenzury

Dyrektywy preprocesora

Preprocesor jest jednym z modułów kompilatora. Działa on zwykle w ukryciu, przez co nie każdy zdaje sobie sprawę z jego istnienia. Zanim jeszcze ruszy do pracy kompilator kod programu jest przeglądany przez preprocesor. Preprocesor bada cały kod w poszukiwaniu specjalnych komend. Owe komendy to właśnie dyrektywy. Za ich pomocą można niejako sterować przebiegiem kompilacji. Dyrektywy można umieszczać w dowolnym miejscu programu. Każda dyrektywa rozpoczyna się znaczkiem płotka [#]. Za koniec dyrektywy uważa się koniec wiersza. Zatem nie trzeba już stosować średnika kończącego. Poniżaj przedstawione zostały jedynie wybrane dyrektywy.

dyrektywa #define

Ta dyrektywa służy do definiowania makrodefinicji. Makrodefinicje stosuje się w celu uproszczenia i skrócenia zapisu. Zamiast za każdym razem pisać długie i skomplikowane wyrażenia można to zrobić jednokrotnie. Wystarczy wówczas owe wyrażenie zapisać, jako makrodefinicje i wywoływać w stosownym momencie. Przypuśćmy, że masz pewien fragment kodu, który stosujesz wielokrotnie w programie. Zajmuje on jedną linijkę, ale jest dość skomplikowany. Wielokrotne wklepywanie lub kopiowanie tego samego tekstu jest trochę męczące. Aby sobie uprzyjemnić życie można zapisać powtarzający się kod, jako makrodefinicję. Składnia wygląda tak:

  1. #define wyrażenie ciąg_znaków_zastępczych

Można powiedzieć, że słowo wyrażenie to jakby nazwa makrodefinicji, natomiast ciąg_znaków_zastępczych to jej wartość. Od tej pory każdorazowe wystąpienie nazwy makrodefinicji zostanie automatycznie zastąpione jej wartością. Zarówno nazwa, jak i wartość mogą być dowolnymi ciągami znakowymi. Należy jednak pamiętać, aby w przypadku kilkuczłonowej nazwy nie oddzielać poszczególnych słów spacją. Chodzi tutaj o taką sytuację:

  1. #define makrodefinicja do obliczania prowizji instrukcje wykonujące tę czynność

Taki zapis zostanie błędnie zinterpretowany przez preprocesor. Konkretniej mówiąc zostanie to odczytane następująco. Słowo makrodefinicja będzie nazwą, natomiast do wartością. Zatem zupełnie coś innego. Aby uniknąć takich błędów najlepiej korzystać z podkreślnika. Poprawnie wyglądałoby to tak:

  1. #define makrodefinicja_do_obliczania_prowizji instrukcje wykonujące tę czynność

Myślę, że jest to zrozumiałe. Jest jeszcze jedna rzecz, o której trzeba pamiętać. Makrodefinicja raz zdefiniowane nie może zostać użyta ponownie. Jest to dość logiczne. Jeżeli masz dwie identyczne makrodefinicje, to jak je rozróżnisz? Nie ma na to sposobu. Zatem raczej trudno tutaj popełnić błąd. Jednak dla formalności o tym powiedziałem.

dyrektywa #undef

Pamiętasz jeszcze, jak mówiłem, że użyta makrodefinicja nie może zostać zdefiniowana na nowo? No cóż.. to nie do końca jest prawdą. Istnieje bowiem sposób, aby użyć tej samej nazwy powtórnie w nowej wersji makrodefinicji. Jednak najpierw należy odwołać istniejącą już makrodefinicję i zdefiniować nową. Aby to zrobić należy skorzystać z dyrektywy #undef. Działa to tak. Przypuśćmy, że mamy zdefiniowaną makrodefinicję o nazwie prowizja, która służy do obliczania prowizji. Nagle stwierdzasz, że taka makrodefinicja Ci nie odpowiada. Postanawiasz zdefiniować makrodefinicję, która wykonuje nieco inne obliczenia. Nie możesz usunąć starej wersji, gdyż znajduje się ona w pliku już skompilowanym. Wówczas jedynym wyjściem jest odwołanie starej wersji makrodefinicji prowizja i zdefiniowanie nowej - ulepszonej. Aby odwołać starą wersję należy napisać:

  1. #undef prowizja

To wystarczy. Preprocesor już zapomniał, że kiedykolwiek istniała makrodefinicja o nazwie prowizja. Zatem użycie jej nazwy spowoduje błąd. Teraz wystarczy zdefiniować nową wersję makrodefinicji. Na koniec chciałem jeszcze zaznaczyć, iż makrodefinicje mają dość mały udział w programowaniu. Zamiast makrodefinicje stosuje się raczej funkcje, gdyż są one znacznie bezpieczniejsze w obsłudze.

dyrektywy #if, #elif, #else i #endif

Wszystkie te dyrektywy służą do kompilacji warunkowej. Załóżmy, że piszesz bardzo uniwersalny program, działający na kilku różnych platformach. W zależności od sprzętu poszczególne moduły programu będą nieco inne. Chcesz teraz sprawdzić działanie programu. Wprowadzasz więc pomocniczą zmienną, której wartość będzie zależna od systemu, na którym kompilujesz program. Za pomocą dyrektyw kompilacji warunkowej możesz określić, która część kodu zostanie skompilowana.

  1. #define SYSTEM 1
  2.  
  3. #if (SYSTEM == 1)
  4. //moduł dla UNIX-a
  5. #elif (SYSTEM == 2)
  6. //moduł dla Solarisa
  7. #elif (SYSTEM == 3)
  8. //moduł dla Windows
  9. #else
  10. //moduł niezdefiniowany!:
  11. #endif

Na początku zdefiniowaliśmy sobie makrodefinicję o nazwie SYSTEM. Jej wartość to jeden. Następnie znajduje się blok kompilacji warunkowej. W zależności od naszej makrodefinicji SYSTEM zostanie skompilowany odpowiedni moduł. U nas SYSTEM wynosi jeden. Zatem skompilowany zostanie moduł dla systemu UNIX. Wartość dwa oznacza moduł dla systemu Solaris, a wartość 3 dla Windows. Każda inna wartość spowoduje, że kompilacja nie odbędzie się. Należy tutaj zaznaczyć, iż wszelkie instrukcje trzeba umieszczać w nowej linijce. Nie mogą one być napisane bezpośrednio za dyrektywą warunkową. Jeżeli nawet o tym zapomnisz kompilator i tak Ci przypomni :) Cały blok trzeba zakończyć dyrektywą #endif.

dyrektywy #ifdef i #ifndef

Te dyrektywy również służą do kompilacji warunkowej. Jednakże są znacznie bardziej praktyczniejsze niż opisane wcześniej. Poprzednio warunkiem była wartość makrodefinicji. Teraz jest nieco inaczej. Warunkiem jest sama makrodefinicja. Konkretnie, jeżeli dana makrodefinicja istnieje, czyli jest zdefiniowana kompilacja nastąpi. Tak jest w przypadku #ifdef. Z dyrektywš #ifndef jest dokładnie odwrotnie.

  1. #define SYSTEM
  2. #ifdef SYSTEM
  3. //instrukcje kompilowane, jeżeli makrodefinicja SYSTEM jest zdefiniowana
  4. #ifndef SYSTEM
  5. //instrukcje kompilowane, jeżeli makrodefinicja SYSTEM nie jest zdefiniowana
  6. #endif

Zauważ, że tym razem makrodefinicja SYSTEM nie posiada żadnej wartości! Posiada jedynie nazwę. Jest to dyrektywa pusta. Następnie znajduje się dyrektywa kompilacji warunkowej. Działa ona tak. Jeżeli makrodefinicja SYSTEM istnieje zostanie skompilowana pierwsza część bloku. Druga część dotyczy sytuacji, gdy makrodefinicja nie jest zdefiniowana. W naszym przypadku SYSTEM istnieje. Wówczas zostaną skompilowane instrukcje umieszczone za dyrektywą #ifdef.

dyrektywa #include

Dyrektywa include jest stosowana chyba najczęściej. Krótko mówiąc jej działanie skupia się na dołączeniu do kompilowanego pliku zawartości innego pliku. Jest przydatna w sytuacjach, kiedy chcemy skorzystać z funkcji bibliotecznych. Funkcje biblioteczne to najzwyklejsze funkcje dostarczone przez producenta kompilatora. Są one skompilowane i gotowe do pracy. Aby z nich skorzystać należy dołączyć do programu określony plik nagłówkowy zawierający deklaracje interesujących nas funkcji. Pamiętasz jeszcze, jak mówiłem o klasach? Tam też zdefiniowaliśmy sobie funkcję do kopiowania stringów. Nadmieniłem też, że tak naprawdę wykonaliśmy zbędną pracę, gdyż taka funkcja już istnieje i znajduje się w pliku z funkcjami bibliotecznymi. Zatem skorzystajmy z niej.

  1. #include <sting.h>
  2. char string1 [30] = "to jest zawartosc stringu1";
  3. char string2 [30];

Za pomocą dyrektywy do naszego programu dołączyliśmy plik string.h. Zawiera on rozmaite funkcje do dokonywania operacji na tablicach znakowych. Zauważ, że plik podajemy w nawiasach trójkątnych, a sam plik ma rozszerzenie '.h'. Takie są rozszerzenia dla plików nagłówkowych zawierających deklaracje funkcji. Teraz już możemy korzystać z tych funkcji.

  1. strcpy (string2, string1);

W wyniku użycia funkcji strcpy zawartość tablicy stringu1 została skopiowana do tablicy string2. Dyrektywa include nie służy jedynie do włączania do programu plików nagłówkowych. Za jej pomocą można także wstawić do programu treść innego pliku. Jest to dość praktyczne w przypadku rozbudowanych programów. Można podzielić projekt na kilka modułów. Każdy moduł umieścić w osobnym pliku i wstawić do programu w odpowiednie miejsce. Ułatwia to późniejsze modyfikacje programu. Wówczas podczas włączania takiego pliku zamiast nawiasów trójkątnych należy zastosować cudzysłów.
Oprócz wymienionych dyrektyw istnieją jeszcze inne. Jednak nie będę Ci zawracał nimi głowy, są mało przydatne. Służą do drobnych modyfikacji, jak wstawienie daty kompilacji. Sam ich jeszcze nigdy nie stosowałem.
Jeżeli jesteś ciekaw, jakie jeszcze funkcje oferuje Ci producent kompilatora to przeglądnij sobie pliki z rozszerzeniem '.h'. Tam właśnie zawarte są deklaracje funkcji bibliotecznych. Niestety ich opisy są w Języku angielskim, jeżeli w ogóle są! :( Na pocieszenie powiem, że w następnym rozdziale pogadamy sobie o wybranych funkcjach bibliotecznych służących do obsługi ekranu, klawiatury oraz dysku.