• Nie Znaleziono Wyników

ścieżka względna do pliku nagłówkowego

W dokumencie Stworzone na Wikibooks, (Stron 90-97)

Podstawowe procedury wejścia i wyjścia

Przykład 4 ścieżka względna do pliku nagłówkowego

Opis: W miejsce linijki zostanie wczytany plik umieszczony w katalogu ,,katalog”, 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/katalog”

#include "katalog1/plik_naglowkowy.h"

Przykład 5 — ścieżka względna do pliku nagłówkowego

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/katalog/katalog/”

#include "../katalog1/katalog2/plik_naglowkowy.h"

Więcej informacji możesz uzyskać w rozdzialeBiblioteki.

12.2.2 #define

Linia pozwalająca zdefiniować stałą, funkcję lub słowo kluczowe, które będzie potem pod-mienione w kodzie programu na odpowiednią wartość lub może zostać użyte w instrukcjach warunkowych dla preprocesora. Składnia:

#define NAZWA_STALEJ WARTOSC

lub

#define NAZWA_STALEJ

12.2. DYREKTYWY PREPROCESORA 91

Przykład

#define LICZBA  — spowoduje ,że każde wystąpienie słowa LICZBA w kodzie zostanie za-stąpione ósemką.

#define SUMA(a,b) (a+b) — spowoduje, ze każde wystąpienie wywołania ,,funkcji” SUMA zo-stanie zastąpione przez sumę argumentów

12.2.3 #undef

Ta instrukcja odwołuje definicję wykonaną instrukcją #define.

#undef STALA

12.2.4 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:

#if wprowadza warunek, który jeśli nie jest prawdziwy powoduje pominięcie kompilowania kodu, aż do napotkania jednej z poniższych instrukcji.

#else spowoduje skompilowanie kodu jeżeli warunek za #if jest nieprawdziwy, aż do napo-tkania któregoś z poniższych instrukcji.

#elif wprowadza nowy warunek, który będzie sprawdzony jeżeli poprzedni był niepraw-dziwy. Stanowi połączenie instrukcji #if i #else.

#endif zamyka blok ostatniej instrukcji warunkowej.

Przykład:

#if INSTRUKCJE == 2

printf ("Podaj liczbę z przedziału 10 do 0\n"); /*1*/

#elif INSTRUKCJE == 1

printf ("Podaj liczbę: "); /*2*/

#else

printf ("Podaj parametr: "); /*3*/

#endif

scanf ("%d\n", &liczba);/*4*/

ˆ wiersz nr  zostanie skompilowany jeżeli stała INSTRUKCJE będzie równa 

ˆ wiersz nr  zostanie skompilowany, gdy INSTRUKCJE będzie równa 

ˆ wiersz nr  zostanie skompilowany w pozostałych wypadkach

ˆ wiersz nr  będzie kompilowany zawsze

#ifdef #ifndef #else #endif

Te instrukcje warunkują kompilację od tego, czy odpowiednia stała została zdefiniowana.

#ifdef spowoduje, że kompilator skompiluje poniższy kod tylko gdy została zdefiniowana odpowiednia stała.

#ifndef ma odwrotne działanie do #ifdef, a mianowicie brak definicji odpowiedniej stałej umożliwia kompilacje poniższego kodu.

#else,#endif mają identyczne zastosowanie jak te z powyższejgrupy

Przykład:

#define INFO /*definicja stałej INFO*/

#ifdef INFO

printf ("Twórcą tego programu jest Jan Kowalski\n");/*1*/

#endif

#ifndef INFO

printf ("Twórcą tego programu jest znany programista\n");/*2*/

#endif

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ć

Twórcą tego programu jest Jan Kowalski

12.2.5 #error

Powoduje przerwanie kompilacji i wyświetlenie tekstu, który znajduje się za tą instrukcją.

Przydatne gdy chcemy zabezpieczyć się przed zdefiniowaniem nieodpowiednich stałych.

Przykład:

#if BLAD == 1

#error "Poważny błąd kompilacji"

#endif

Co jeżeli zdefiniujemy stałą BLAD z wartością ? Spowoduje to wyświetlenie w trakcie kom-pilacji komunikatu podobnego do poniższego:

Fatal error program.c 6: Error directive: "Poważny błąd kompilacji"

in function main()

*** 1 errors in Compile ***

wraz z przerwaniem kompilacji.

12.2.6 #warning

Wyświetla tekst, zawarty w cudzysłowach, jako ostrzeżenie. Jest często używany do sygna-lizacji programiście, że dana część programu jest przestarzała lub może sprawiać problemy.

12.2. DYREKTYWY PREPROCESORA 93 Przykład:

#warning "To jest bardzo prosty program"

Spowoduje to takie oto zachowanie kompilatora:

test.c:3:2: warning: #warning "To jest bardzo prosty program"

Użycie dyrektywy #warning nie przerywa procesu kompilacji i służy tylko do wyświetlania komunikatów dla programisty w czasie kompilacji programu.

12.2.7 #line

Powoduje wyzerowanie licznika linii kompilatora, który jest używany przy wyświetlaniu opisu błędów kompilacji. Pozwala to na szybkie znalezienie możliwej przyczyny błędu w rozbudowanym programie.

Przykład:

printf ("Podaj wartość funkcji");

#line

printf ("W przedziale od 10 do 0\n); /* tutaj jest błąd - brak cudzysłowu zamykającego */

Jeżeli teraz nastąpi próba skompilowania tego kodu to kompilator poinformuje, że wystąpił błąd składni w linii , a nie np. .

12.2.8 Makra

Preprocesor języka C umożliwia też tworzenie makr, czyli automatycznie wykonywanych czynności. Makra deklaruje się za pomocą dyrektywy #define:

#define MAKRO(arg1, arg2, ...) (wyrażenie)

W momencie wystąpienia MAKRA w tekście, preprocesor automatycznie zamieni makro na wyrażenie. Makra mogą być pewnego rodzaju alternatywami dla funkcji, ale powinno się ich używać tylko w specjalnych przypadkach. Ponieważ makro sprowadza się do pro-stego 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 wy-wołanie funkcji, które zawsze zajmuje trochę czasu1), ale też mniej bezpieczne i elastyczne niż funkcje.

Przeanalizujmy teraz fragment kodu:

#include <stdio.h>

#define KWADRAT(x) ((x)*(x))

int main () {

printf ("2 do kwadratu wynosi %d\n", KWADRAT(2));

return 0;

}

1Tak naprawdę wg standardu C99 istnieje możliwość napisania funkcji, której kod także będzie wstawiany w miejscu wywołania. Odbywa się to dziękiinline.

Preprocesor w miejsce wyrażeniaKWADRAT(2) wstawił((2)*(2)). Zastanówmy się, co stałoby się, gdybyśmy napisaliKWADRAT("2"). Preprocesor po prostu wstawi napis do kodu, co da wyrażenie(("2")*("2")), które jest nieprawidłowe. Kompilator zgłosi błąd, ale pro-gramista 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 argumen-tów.

Nawet jeżeli program się skompiluje to makro może dawać nieoczekiwany wynik. Jest tak w przypadku poniższego kodu:

int x = 1;

int y = KWADRAT(++x);

Dzieje się tak dlatego, że makra rozwijane są przez preprocesor i kompilator widzi kod:

int x = 1;

int y = ((++x)*(++x));

Również poniższe makra są błędne pomimo, że opisany problem w nich nie występuje:

#define SUMA(a, b) a + b

#define ILOCZYN(a, b) a * b

Dają one nieoczekiwane wyniki dla wywołań:

SUMA(2, 2) * 2; /* 6 zamiast 8 */

ILOCZYN(2 + 2, 2 + 2); /* 8 zamiast 16 */

Z tego powodu istotne jest użycie nawiasów:

#define SUMA(a, b) ((a) + (b))

#define ILOCZYN(a, b) ((a) * (b))

12.2.9 # oraz ##

Dość ciekawe możliwości ma w makrach znak ,,#”. Zamienia on stojący za nim identyfikator na napis.

#include <stdio.h>

#define wypisz(x) printf("%s=%i\n", #x, x)

int main() {

int i=1;

char a=5;

wypisz(i);

wypisz(a);

return 0;

}

Program wypisze:

i=1 a=5

12.3. PREDEFINIOWANE MAKRA 95 Czyliwypisz(a)jest rozwijane wprintf("%s=%i\n", a", a).

Natomiast znaki ,,##” łączą dwie nazwy w jedną. Przykład:

#include <stdio.h>

#define abc(x) int zmienna ## x

int main() {

abc(nasza); /* dzięki temu zadeklarujemy zmienną o nazwie zmiennanasza */

zmiennanasza = 2;

return 0;

}

Więcej o dobrych zwyczajach w tworzeniu makr można się dowiedzieć w rozdziale Po-wszechne praktyki.

12.3 Predefiniowane makra

W języku wprowadzono również serię predefiniowanych makr, które mają ułatwić życie pro-gramiście. Oto one:

ˆ DATE — data w momencie kompilacji

ˆ TIME — godzina w momencie kompilacji

ˆ FILE — łańcuch, który zawiera nazwę pliku, który aktualnie jest kompilowany przez kompilator

ˆ LINE — definiuje numer linijki

ˆ STDC — w kompilatorach zgodnych ze standardem ANSI lub nowszym makro to przyjmuje wartość 

ˆ STDC VERSION — zależnie od poziomu zgodności kompilatora makro przyjmuje różne wartości:

– jeżeli kompilator jest zgodny z ANSI (rok ) makro nie jest zdefiniowane, – jeżeli kompilator jest zgodny ze standardem z  makro ma wartość199409L, – jeżeli kompilator jest zgodny ze standardem z  makro ma wartość199901L. Warto również wspomnieć o identyfikatorze func zdefiniowanym w standardzie C, którego wartość to nazwa funkcji.

Spróbujmy użyć tych makr w praktyce:

#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__)

#else

/* Jezeli __func__ nie ma, to go nie używamy */

# define BUG(message) fprintf(stderr, "%s:%d: %s\n", \ __FILE__, __LINE__, message)

#endif

int main(void) {

printf("Program ABC, data kompilacji: %s %s\n", __DATE__, __TIME__);

BUG("Przykladowy komunikat bledu");

return 0;

}

Efekt działania programu, gdy kompilowany jest kompilatorem C:

Program ABC, data kompilacji: Sep 1 2008 19:12:13 test.c:17: Przykladowy komunikat bledu (w funkcji main)

Gdy kompilowany jest kompilatorem ANSI C:

Program ABC, data kompilacji: Sep 1 2008 19:13:16 test.c:17: Przykladowy komunikat bledu

Rozdział 13

W dokumencie Stworzone na Wikibooks, (Stron 90-97)