[ Pobierz całość w formacie PDF ]

pisanym identyfikatorowi CASE. %7łeby przekazać każdej frazie CASE wartość wyra-
żenia podanego w identyfikatorze SWITCH skorzystamy ze zmiennej lokalnej
#define SWITCH(x) do{ char *_ptr=x;
#define CASE(y) if(!strcmp(_ptr,y)){
Tekst przypisany identyfikatorowi SWITCH rozpoczyna pętlę do while. Umiesz-
czenie całości w pętli służy tylko temu, żeby móc używać instrukcji break do na-
tychmiastowego wyjścia z konstrukcji SWITCH.
#define END_SWITCH }while(0);
#define BREAK break; }
Pozostaje jeszcze tylko zdefiniować identyfikator DEFAULT
#define DEFAULT {
Nawias { służący tutaj wyłącznie sparowaniu nawiasu powstałego z rozwinięcia
identyfikatora BREAK kończącego przypadek default.
Komplet definicji znajduje się w pliku switch.h.
Jak widać, definicje są proste i tworzą całkiem przyzwoity odpowiednik instruk-
cji switch. Jako przykład ich zastosowania przedstawiam poniżej fragment kodu
rozpoznający argumenty wywołania programu.
#include
#include "switch.h"
/* definicje zmiennych */
main(int argc,char *argv[]) /* uproszczona UNIX-owa funkcja
find */
{
if(argc
programu = 4 */
{
fprintf(stderr,"find pathname expression\n");
return;
}
II. Preprocesor 31
path=argv[1]; /* pierwszy argument to
ścieżka */
for(x=2;x
szukać */
SWITCH(argv[x])
{
CASE("-name")
file=argv[++x]; /* po -name ma być nazwa pliku */
BREAK;
CASE("-type")
filetype=*argv[++x]; /* typ pliku: jedna litera */
BREAK;
CASE("-group")
gname=argv[++x]; /* nazwa grupy użytkowników */
BREAK;
CASE("-user")
uname=argv[++x]; /* nazwa właściciela pliku */
BREAK;
CASE("-size")
size=atoi(argv[++x]); /* rozmiar pliku */
BREAK;
CASE("-atime")
atime=atoi(argv[++x]); /* kiedy był uruchomiany */
BREAK;
/* ... */ /* i tak dalej ... */
DEFAULT
fprintf(stderr,"find pathname expression\n");
return;
BREAK; /* inna sekwencja oznacza błąd */
}
END_SWITCH;
/* ... */
}
Wiele przykładów praktycznego zastosowania definicji preprocesora zawiera
rozdział tej książki pod tytułem "Programowanie współbieżne"
32 Wgłąb języka C
III. Formatowane wejście/wyjście
Język C nie posiada żadnych instrukcji wejścia/wyjścia. Przenośnym sposobem
realizacji komunikacji programu ze światem zewnętrznym jest korzystanie ze
standardowych funkcji bibliotecznych ANSI, których sposób działania jest ściśle
zdefiniowany. Do kompilatora zgodnego ze standardem ANSI dołączone są bi-
blioteki zawierające definicje tych funkcji i zawsze, niezależnie od kompilatora
czy komputera, działają one tak samo. Ta pełna przenośność powoduje czasami,
iż zapomina się o tym, że są to jednak funkcje, które w żaden sposób nie różnią
się od innych funkcji napisanych w języku C4).
Pisząc o standardowych funkcjach formatowanego wyjścia mam na myśli funk-
cję printf i jej pochodne (zarówno te zdefiniowane w bibliotekach standardu ANSI,
jak i te, które stanowią rozszerzenie tego standardu i są dostępne w niektórych
kompilatorach). Funkcje formatowanego wejścia to funkcja scanf oraz rodzina jej
pochodnych. Funkcje formatowanego we/wy są wśród standardowych funkcji wej-
ścia/wyjścia funkcjami najwyższego poziomu. W rozdziale tym będę używał nazw
"printf" i "scanf" zarówno do określenia konkretnych funkcji printf i scanf jak
i całej rodziny funkcji pochodnych.
Funkcje printf i scanf są funkcjami o zmiennej liczbie argumentów. Ponieważ
w dalszej części opisu będę często odwoływał się do pojęć związanych z takimi
funkcjami, omówię teraz krótko mechanizm ich działania i sposób definiowania.
1.Funkcje ze zmienną liczbą argumentów
Mimo, że w języku C można tworzyć funkcje pobierające zmienną liczbę argu-
mentów, to składnia języka nie zawiera pełnych mechanizmów umożliwiających
ich użycie. Jedyną rzeczą, jaką zapewnia język C, jest możliwość zdefiniowania
funkcji, dla której nie będzie sprawdzane, czy liczba argumentów w wywołaniu
jest równa liczbie zadeklarowanych parametrów. Natomiast pobieranie argumen-
tów funkcji o zmiennej ich liczbie musi być rozwiązane przez programistę, na po-
ziomie bezpośrednich odwołań do pamięci. Mimo, że istnieje standard takich
odwołań i w każdym kompilatorze dostępne są odpowiednie makrodefinicje, uwa-
żam (i postaram się to poniżej udowodnić), że używając tych makrodefinicji trzeba
zdawać sobie sprawę jak one działają i co robią. Dlatego też poniższy opis będzie
zawierał wiele szczegółów dotyczących sposobu realizacji przez kompilator pew-
nych rzeczy, których normalnie programista nie musi znać.
Ogólnie przyjętym rozwiązaniem, stosowanym nie tylko w języku C, jest prze- [ Pobierz całość w formacie PDF ]