C/Preprocesor: Różnice pomiędzy wersjami

Usunięta treść Dodana treść
m Update syntaxhighlight tags - remove use of deprecated <source> tags
Linia 26:
==Dyrektywy preprocesora==
Dyrektywy preprocesora są to wyrażenia, które zapoczątkowane są 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 "\":
<sourcesyntaxhighlight lang="c">
#define ADD(a,b) \
a+b
</syntaxhighlight>
</source>
 
Omówimy teraz kilka ważniejszych dyrektyw.
Linia 37:
 
Przykład 1 {{r|przypis1}}
<sourcesyntaxhighlight lang="c">
#include <plik_nagłówkowy_do_dołączenia>
</syntaxhighlight>
</source>
 
Przykład 2
<sourcesyntaxhighlight lang="c">
#include "plik_nagłówkowy_do_dołączenia"
</syntaxhighlight>
</source>
 
Jeżeli nazwa pliku nagłówkowego będzie ujęta w nawiasy ostre (przykład 1), to kompilator poszuka go wśród własnych plików nagłówkowych (które najczęściej się znajdują w podkatalogu "includes" w katalogu kompilatora). Jeśli jednak nazwa ta będzie ujęta w podwójne cudzysłowy(przykład 2), to kompilator poszuka jej w katalogu, w którym znajduje się kompilowany plik (można zmienić to zachowanie w opcjach niektórych kompilatorów). Przy użyciu tej dyrektywy można także wskazać dokładne położenie plików nagłówkowych poprzez wpisanie bezwzględnej lub względnej ścieżki dostępu do tego pliku nagłówkowego.
Linia 51:
Opis: W miejsce jednej i drugiej linijki zostanie wczytany plik umieszczony w danej lokalizacji
 
<sourcesyntaxhighlight lang="c">
#include "/usr/include/plik_nagłówkowy.h"
#include "C:\\borland\includes\plik_nagłówkowy.h"
</syntaxhighlight>
</source>
 
 
Przykład 4 - ścieżka względna do pliku nagłówkowego<br>
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"
<sourcesyntaxhighlight lang="c">
#include "katalog1/plik_naglowkowy.h"
</syntaxhighlight>
</source>
 
 
Przykład 5 - ścieżka względna do pliku nagłówkowego<br>
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/"
<sourcesyntaxhighlight lang="c">
#include "../katalog1/katalog2/plik_naglowkowy.h"
</syntaxhighlight>
</source>
 
Więcej informacji możesz uzyskać w rozdziale [[C/Biblioteki|Biblioteki]].
Linia 82:
Składnia:
 
<sourcesyntaxhighlight lang="c">
#define NAZWA_STALEJ WARTOSC
</syntaxhighlight>
</source>
 
lub
<sourcesyntaxhighlight lang="c">
#define NAZWA_STALEJ
</syntaxhighlight>
</source>
 
Przykład:<br>
Linia 96:
 
Jeśli w miejscu wartości znajduje się wyrażenie, to należy je umieścić w '''nawiasach'''.
<sourcesyntaxhighlight lang="c">
#define A 5
#define B ((2)+(A))
</syntaxhighlight>
</source>
 
Unikniemy w ten sposób [[C/Powszechne_praktyki#Konwencje_pisania_makr|niespodzianek]] związanych z priorytetem operatorów :
 
<sourcesyntaxhighlight lang="c">
/*
 
Linia 123:
 
}
</syntaxhighlight>
</source>
 
Po skompilowaniu i uruchomieniu programu otrzymujemy:
Linia 148:
Ta instrukcja odwołuje definicję wykonaną instrukcją ''#define''.
 
<sourcesyntaxhighlight lang="c">#undef STALA</sourcesyntaxhighlight>
 
===instrukcje warunkowe===
Linia 162:
 
Przykład:
<sourcesyntaxhighlight lang="c">
#if INSTRUKCJE == 2
printf ("Podaj liczbę z przedziału 10 do 0\n"); /*1*/
Linia 171:
#endif
scanf ("%d", &liczba); /*4*/
</syntaxhighlight>
</source>
*wiersz nr 1 zostanie skompilowany jeżeli stała INSTRUKCJE będzie równa 2
*wiersz nr 2 zostanie skompilowany, gdy INSTRUKCJE będzie równa 1
Linia 185:
 
Przykład:
<sourcesyntaxhighlight lang="c">
#define INFO /*definicja stałej INFO*/
#ifdef INFO
Linia 193:
printf ("Twórcą tego programu jest znany programista\n");/*2*/
#endif
</syntaxhighlight>
</source>
To czy dowiemy się kto jest twórcą tego programu zależy czy instrukcja definiująca stałą INFO będzie istnieć. W powyższym przypadku na ekranie powinno się wyświetlić
 
Linia 203:
 
Przykład:
<sourcesyntaxhighlight lang="c">
#ifdef BLAD
#error Poważny błąd kompilacji
#endif
</syntaxhighlight>
</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:
Fatal error program.c 6: Error directive: Poważny błąd kompilacji in function main()<br>
Linia 218:
Przykład:
 
<sourcesyntaxhighlight lang="c">
#warning To jest bardzo prosty program
</syntaxhighlight>
</source>
 
Spowoduje to takie oto zachowanie kompilatora:
Linia 230:
 
Przykład:
<sourcesyntaxhighlight lang="c">
printf ("Podaj wartość funkcji");
#line
printf ("W przedziale od 10 do 0\n); /* tutaj jest błąd - brak cudzysłowu zamykającego */
</syntaxhighlight>
</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'''.
 
Linia 243:
===# oraz ##===
Dość ciekawe możliwości ma w makrach znak "#". Zamienia on stojący za nim identyfikator na napis.
<sourcesyntaxhighlight lang="c">
#include <stdio.h>
#define wypisz(x) printf("%s=%i\n", #x, x)
Linia 255:
return 0;
}
</syntaxhighlight>
</source>
 
Program wypisze:
Linia 264:
 
Natomiast znaki "##" łączą dwie nazwy w jedną. Przykład:
<sourcesyntaxhighlight lang="c">
#include <stdio.h>
 
Linia 278:
return 0;
}
</syntaxhighlight>
</source>
 
Program wypisze:
Linia 291:
 
Makra deklaruje się za pomocą dyrektywy #define:
<sourcesyntaxhighlight lang="c">
#define MAKRO(arg1, arg2, ...) (wyrażenie)
/* można również napisać: do {instrukcje} while(0) */
/* lub jeśli jest tylko jedna instrukcja można napisać: instrukcja (bez średnika!) */
</syntaxhighlight>
</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:
<sourcesyntaxhighlight lang="c">
#include <stdio.h>
#define KWADRAT(x) ((x)*(x))
Linia 309:
return 0;
}
</syntaxhighlight>
</source>
 
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:
<sourcesyntaxhighlight lang="c">
int x = 1;
int y = KWADRAT(++x);
</syntaxhighlight>
</source>
 
Dzieje się tak dlatego, że makra rozwijane są przez preprocesor i kompilator widzi kod:
<sourcesyntaxhighlight lang="c">
int x = 1;
int y = ((++x)*(++x));
</syntaxhighlight>
</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: Macros_and_Miscellaneous_Pitfalls]</ref> pomimo, że opisany problem w nich nie występuje:
<sourcesyntaxhighlight lang="c">
#define SUMA(a, b) a + b
#define ILOCZYN(a, b) a * b
</syntaxhighlight>
</source>
 
Dają one nieoczekiwane wyniki dla wywołań:
<sourcesyntaxhighlight lang="c">
SUMA(2, 2) * 2; /* 6 zamiast 8 */
ILOCZYN(2 + 2, 2 + 2); /* 8 zamiast 16 */
</syntaxhighlight>
</source>
 
Z tego powodu istotne jest użycie nawiasów:
<sourcesyntaxhighlight lang="c">
#define SUMA(a, b) ((a) + (b))
#define ILOCZYN(a, b) ((a) * (b))
</syntaxhighlight>
</source>
 
 
Linia 364:
 
Spróbujmy użyć tych makr w praktyce:
<sourcesyntaxhighlight lang="c">
#include <stdio.h>
 
Linia 383:
return 0;
}
</syntaxhighlight>
</source>
 
Efekt działania programu, gdy kompilowany jest kompilatorem C99: