C/Preprocesor: Różnice pomiędzy wersjami
Usunięta treść Dodana treść
→Wstęp: Poprawiona literówka Znaczniki: Z urządzenia mobilnego Z wersji mobilnej (przeglądarkowej) |
kolorowanie składni, zbędne spacje, usunięto interwiki - są na Wikidanych, zbędny nawias na końcu |
||
Linia 1:
==Wstęp==
'''Preprocesor''' jest to program, który analizuje plik źródłowy (
W języku C wszystkie linijki zaczynające się od symbolu "#" są [[C/Preprocesor#Dyrektywy_preprocesora|instrukcjami ( dyrektywami) dla preprocesora]]. Nie są elementami języka C i nie podlegają bezpośrednio procesowi kompilacji.
Preprocesor może być
* standardowy = element kompilatora
* niestandardowy <ref>[http://stackoverflow.com/questions/396644/replacements-for-the-c-preprocessor stackoverflow questions
** CPIP napisany w Pythonie
** GNU M4
** Cog
** Wave parser (
** własny program napisany w
*** Lua
*** [[PHP]], uruchamiamy z [[PHP/Pierwszy_skrypt#Konsola|konsoli]]
Linia 27:
Dyrektywy preprocesora są to wyrażenia, które występują zaraz za symbolem "#" i to właśnie za ich pomocą możemy używać preprocesora. Dyrektywa zaczyna się od znaku # i kończy się wraz z końcem linii. Aby przenieść dalszą część dyrektywy do następnej linii, należy zakończyć linię znakiem "\":
<source lang="c">
</source>
Omówimy teraz kilka ważniejszych dyrektyw.
===#include===
Najpopularniejsza dyrektywa, wstawiająca w swoje miejsce treść pliku podanego w nawiasach ostrych lub cudzysłowie. Składnia:
Linia 37 ⟶ 38:
Przykład 1 {{r|przypis1}}
<source lang="c">
</source>
Przykład 2
<source lang="c">
</source>
Linia 51 ⟶ 52:
<source lang="c">
</source>
Linia 59 ⟶ 60:
Opis: W miejsce linijki zostanie wczytany plik umieszczony w katalogu "katalog1", a ten katalog jest w katalogu z plikiem źródłowym. Inaczej mówiąc, jeśli plik źródłowy jest w katalogu "/home/user/dokumenty/zrodla", to plik nagłówkowy jest umieszczony w katalogu "/home/user/dokumenty/zrodla/katalog1"
<source lang="c">
</source>
Linia 66 ⟶ 67:
Opis: Jeśli plik źródłowy jest umieszczony w katalogu "/home/user/dokumenty/zrodla", to plik nagłówkowy znajduje się w katalogu "/home/user/dokumenty/katalog1/katalog2/"
<source lang="c">
</source>
Linia 72 ⟶ 73:
===#define===
Linia pozwalająca zdefiniować
* stałą,
* funkcję
Linia 82 ⟶ 83:
<source lang="c">
</source>
lub
<source lang="c">
</source>
Linia 95 ⟶ 96:
Jeśli w miejscu wartości znajduje się wyrażenie, to należy je umieścić w '''nawiasach'''.
</source>
Unikniemy w ten sposób [[C/Powszechne_praktyki#Konwencje_pisania_makr|niespodzianek]] związanych z priorytetem operatorów :
<source lang="c">
/*
Linia 120 ⟶ 122:
return 0;
▲}</source>
</source>
Po skompilowaniu i uruchomieniu programu otrzymujemy
6 * 9 = 42
a powinno być
6 * 9 = 54
Przyczyną błędu jest interpretacja wyrażenia
1+5*8+1
Ze względu na brak nawiasów i priorytet operatorów (
1+(5*8)+1
a nie jak
(1+5)*(8+1)
Linia 145 ⟶ 148:
Ta instrukcja odwołuje definicję wykonaną instrukcją ''#define''.
===instrukcje warunkowe===
Preprocesor zawiera również instrukcje warunkowe, pozwalające na wybór tego co ma zostać skompilowane w zależności od tego, czy stała jest zdefiniowana lub jaką ma wartość:
====#if #elif #else #endif====
Te instrukcje uzależniają kompilacje od warunków. Ich działanie jest podobne do instrukcji warunkowych w samym języku C. I tak:
Linia 159 ⟶ 163:
Przykład:
<source lang="c">
</source>
*wiersz nr 1 zostanie skompilowany jeżeli stała INSTRUKCJE będzie równa 2
Linia 200 ⟶ 204:
Przykład:
<source lang="c">
</source>
Co jeżeli zdefiniujemy stałą BLAD, oczywiście przy pomocy dyrektywy #define? Spowoduje to wyświetlenie w trakcie kompilacji komunikatu podobnego do poniższego:
Linia 215 ⟶ 219:
<source lang="c">
</source>
Linia 226 ⟶ 230:
Przykład:
<source lang="c">
</source>
Jeżeli teraz nastąpi próba skompilowania tego kodu to kompilator poinformuje, że wystąpił błąd składni w linii '''1''', a nie np. '''258'''.
===#pragma===
Dyrektywa pragma (od angielskiego
===# oraz ##===
Dość ciekawe możliwości ma w makrach znak "#". Zamienia on stojący za nim identyfikator na napis.
<source lang="c">
</source>
Program wypisze:
Linia 257 ⟶ 264:
Natomiast znaki "##" łączą dwie nazwy w jedną. Przykład:
<source lang="c">
</source>
Więcej o dobrych zwyczajach w tworzeniu makr można się dowiedzieć w rozdziale
Linia 272 ⟶ 280:
Natomiast znaki "# #" łączą dwie nazwy w jedną. Przykład:
<source lang="c">
#include <stdio.h>
Linia 287 ⟶ 296:
return 0;
}
</source>
nasza_zmienna=2
==Makra==
Preprocesor języka C umożliwia też tworzenie makr, czyli automatycznie wykonywanych czynności. Makra deklaruje się za pomocą dyrektywy #define:
<source lang="c">
</source>
W momencie wystąpienia MAKRA w tekście, preprocesor automatycznie zamieni makro na wyrażenie lub instrukcje. Makra mogą być pewnego rodzaju alternatywami dla funkcji, ale powinno się ich używać tylko w specjalnych przypadkach. Ponieważ makro sprowadza się do prostego zastąpienia przez preprocesor wywołania makra przez jego tekst, jest bardzo podatne na trudne do zlokalizowania błędy (kompilator będzie podawał błędy w miejscach, w których nic nie widzimy - bo preprocesor wstawił tam tekst). Makra są szybsze (nie następuje wywołanie funkcji, które zawsze zajmuje trochę czasu{{r|przypis2}}), ale też mniej bezpieczne i elastyczne niż funkcje.
Przeanalizujmy teraz fragment kodu:
<source lang="c">
▲ #include <stdio.h>
▲ #define KWADRAT(x) ((x)*(x))
▲ int main ()
}
▲ {
</source>
▲ printf ("2 do kwadratu wynosi %d\n", KWADRAT(2));
▲ return 0;
▲ }
Preprocesor w miejsce wyrażenia <tt>KWADRAT(2)</tt> wstawił <tt>((2)*(2))</tt>. Zastanówmy się, co stałoby się, gdybyśmy napisali <tt>KWADRAT("2")</tt>. Preprocesor po prostu wstawi napis do kodu, co da wyrażenie <tt>(("2")*("2"))</tt>, które jest nieprawidłowe. Kompilator zgłosi błąd, ale programista widzi tylko w kodzie użycie makra a nie prawdziwą przyczynę błędu. Widać tu, że bezpieczniejsze jest użycie funkcji, które dają możliwość wyspecyfikowania typów argumentów.
Nawet jeżeli program się skompiluje to makro może dawać nieoczekiwany wynik. Jest tak w przypadku poniższego kodu:
<source lang="c">
</source>
Dzieje się tak dlatego, że makra rozwijane są przez preprocesor i kompilator widzi kod:
<source lang="c">
</source>
Również poniższe makra są błędne<ref>[http://www.brainbell.com/tutors/c/Advice_and_Warnings_for_C/Macros_and_Miscellaneous_Pitfalls.html brainbell
▲ int x = 1;
<source lang="c">
▲ int y = ((++x)*(++x));
▲Również poniższe makra są błędne<ref>[http://www.brainbell.com/tutors/c/Advice_and_Warnings_for_C/Macros_and_Miscellaneous_Pitfalls.html brainbell : Macros_and_Miscellaneous_Pitfalls]</ref> pomimo, że opisany problem w nich nie występuje:
</source>
▲ #define SUMA(a, b) a + b
▲ #define ILOCZYN(a, b) a * b
Dają one nieoczekiwane wyniki dla wywołań:
<source lang="c">
</source>
Z tego powodu istotne jest użycie nawiasów:
<source lang="c">
</source>
▲ #define SUMA(a, b) ((a) + (b))
▲ #define ILOCZYN(a, b) ((a) * (b))
=== Predefiniowane makra ===
Linia 359 ⟶ 373:
Spróbujmy użyć tych makr w praktyce:
<source lang="c">
▲ #include <stdio.h>
▲ #if __STDC_VERSION__ >= 199901L
▲ /*Jezeli mamy do dyspozycji identyfikator __func__ wykorzystajmy go.*/
▲ #define BUG(message) fprintf(stderr, "%s:%d: %s (w funkcji %s)\n", \
__FILE__, __LINE__, message, __func__)
__FILE__, __LINE__, message)
</source>
Linia 394 ⟶ 407:
[[../Biblioteka standardowa/]]|
}}
</noinclude>
[[Kategoria:C]]
|