Jezyk ANSI C Programowanie cwiczenia Wydanie II cwjans


Język ANSI C. Programowanie.
Ćwiczenia. Wydanie II
Autorzy: Clovis L. Tondo, Scott E. Gimpel
Tłumaczenie: Paweł Koronkiewicz
ISBN: 978-83-246-2591-8
Tytuł oryginału: The C Answer Book, (2nd Edition)
Format: 158×235, stron: 168
Książka  Język ANSI C. Programowanie. Wydanie II to jedna z najlepszych dostępnych
na rynku pozycji do nauki tego języka, zaliczana do klasyki literatury informatycznej
i ciesząca się niemalejącą popularnością. Przejrzyście opisana teoria, liczne przykłady
oraz zbiór ćwiczeń to atuty doceniane przez kolejne pokolenia programistów.
Niniejsza książka zawiera rozwiązania wszystkich ćwiczeń zawartych w  Języku ANSI
C. Programowanie. Wydanie II . Oprócz działającego i przetestowanego kodu znajdziesz
w niej komentarze do specyficznych konstrukcji i samego sposobu rozwiązywania zadań.
Połączenie teorii z praktyką pozwoli Ci błyskawicznie przyswoić wiedzę na temat języka
C, a następnie wykorzystać ją w praktyce. Ponadto część rozwiązań z pewnością przyda
się w codziennej pracy, dlatego też książka ta sprawdzi się zarówno w rękach adepta
języka C, jak i zawodowego programisty.
Spis tre ci
Wst p 5
Rozdzia 1. Wprowadzenie 7
Rozdzia 2. Typy, operatory i wyra enia 37
Rozdzia 3. Sterowanie wykonywaniem programu 49
Rozdzia 4. Funkcje i struktura programu 57
Rozdzia 5. Wska niki i tablice 77
Rozdzia 6. Struktury 121
Rozdzia 7. Wej cie i wyj cie 135
Rozdzia 8. Interfejs systemu UNIX 149
Skorowidz 161
Rozdzia 4.
Funkcje
i struktura programu
wiczenie 4.1 (str. 89)
Napisz funkcj strrindex(s,t), która zwraca pozycj ostatniego wyst pienia t w s lub -1,
je eli wyszukiwany ci g nie zosta znaleziony.
/* strrindex: zwraca index ostatniego wyst pienia t w s lub  1, je eli nie wyst puje */
int strrindex(char s[], char t[])
{
int i, j, k, pos;
pos = -1;
for (i = 0; s[i] != '\0'; i++) {
for (j=i, k=0; t[k]!='\0' && s[j]==t[k]; j++, k++)
;
if (k > 0 && t[k] == '\0')
pos = i;
}
return pos;
}
Funkcja strrindex jest podobna do strindex, przedstawionej w podrozdziale 4.1 pod-
r cznika K&R. Gdy funkcja strindex znajduje dopasowany podci g, zwraca jego pozycj ,
która jest pozycj pierwszego wyst pienia t w s. Funkcja strrindex nie zwraca pozycji
znalezionego podci gu, ale kontynuuje wyszukiwanie, poniewa jej zadaniem jest okre-
lenie po o enia ostatniego wyst pienia t w s:
if (k > 0 && t[k] == '\0')
pos = i;
J zyk ANSI C. Programowanie. wiczenia
Ten sam problem mo na rozwi za tak e nast puj co:
#include
/* strrindex: zwraca index ostatniego wyst pienia t w s lub  1, je eli nie wyst puje */
int strrindex(char s[], char t[])
{
int i, j, k;
for (i = strlen(s)  strlen(t); i >= 0; i--) {
for (j=i, k=0; t[k]!='\0' && s[j]==t[k]; j++, k++)
;
if (k > 0 && t[k] == '\0')
return i;
}
return -1;
}
Jest to rozwi zanie efektywniejsze. Przegl danie ci gu rozpoczyna si od ko ca ci gu
s minus d ugo ci gu t. Brak dopasowania powoduje przesuni cie wyszukiwania o jedn
pozycj w stron pocz tku ci gu. Gdy tylko funkcja znajduje t w s, zwraca bie c
pozycj , i. Jest to ostatnie wyst pienie t w s.
wiczenie 4.2 (str. 91)
Dodaj do funkcji atof mo liwo obs ugi notacji wyk adniczej, postaci:
123.45e-6
gdzie po liczbie zmiennoprzecinkowej mo e wyst pi litera e lub E i wyk adnik, z opcjo-
nalnym znakiem.
#include
/* atof: konwertuje ci g znaków s na liczb double */
double atof(char s[])
{
double val, power;
int exp, i, sign;
for (i = 0; isspace(s[i]); i++) /* pomi bia e znaki */
;
sign = (s[i] == '-') ? -1 : 1;
if (s[i] == '+' || s[i] == '-')
i++;
for (val = 0.0; isdigit(s[i]); i++)
val = 10.0 * val + (s[i] - '0');
if (s[i] == '.')
i++;
for (power = 1.0; isdigit(s[i]); i++) {
58
Rozdzia 4. " Funkcje i struktura programu
val = 10.0 * val + (s[i] - '0');
power *= 10;
}
val = sign * val / power;
if (s[i] == 'e' || s[i] == 'E') {
sign = (s[++i] == '-') ? -1 : 1;
if (s[i] == '+' || s[i] == '-')
i++;
for (exp = 0; isdigit(s[i]); i++)
exp = 10 * exp + (s[i]  '0');
if (sign == 1)
while (exp-- > 0) /* wyk adnik dodatni */
val *= 10;
else
while (exp-- > 0) /* wyk adnik ujemny */
val /= 10;
}
return val;
}
Pierwsza cz funkcji to powtórzenie funkcji atof z podrozdzia u 4.2 podr cznika K&R.
Funkcja pomija bia e znaki, zapisuje znak i oblicza liczb . Pobieranie liczby z kropk
dziesi tn wymaga identycznej procedury niezale nie od tego, czy w dalszej cz ci
pojawi si wyk adnik.
Druga cz funkcji odpowiada za konwersj opcjonalnego wyk adnika. Je eli ta cz
liczby nie wyst puje, funkcja zwraca warto zapisan w val. Je eli wyk adnik jest obecny,
to jego znak zostaje zapisany w zmiennej sign, po czym warto zostaje obliczona
i zapisana w zmiennej exp.
Ko cowa operacja
if (sign == 1)
while (exp-- > 0)
val *= 10;
else
while (exp-- > 0)
val /= 10;
modyfikuje liczb odpowiednio do ustalonej wcze niej warto ci wyk adnika. Je eli
wyk adnik jest dodatni, liczba zostaje pomno ona exp razy przez 10. Je eli wyk adnik
jest ujemny, liczba zostaje podzielona exp razy przez 10. W zmiennej val zostaje za-
pisany wynik, który jest zwracany do programu wywo uj cego funkcj .
Zmienna val jest dzielona przez 10, a nie mno ona przez 0.1, poniewa liczba 0,1 nie
jest w zapisie binarnym dok adna. Na wi kszo ci komputerów warto 0,1 jest repre-
zentowana jako nieco mniejsza ni 0,1. W efekcie mno enie 10.0*0.1 rzadko daje
wynik 1.0. Powtarzanie dzielenia przez 10 jest wi c lepszym rozwi zaniem ni po-
wtarzanie mno enia przez 0,1, cho utrata dok adno ci wci wyst puje.
59
J zyk ANSI C. Programowanie. wiczenia
wiczenie 4.3 (str. 97)
W oparciu o schemat przedstawiony w przyk adach program kalkulatora mo na atwo
rozbudowywa . Dodaj obs ug operatora modulo (%) i obs ug liczb ujemnych.
#include
#include /* dla atof() */
#define MAXOP 100 /* dopuszczalny rozmiar operandu lub operatora */
#define NUMBER '0' /* sygna , e pobrano liczb */
int getop(char []);
void push(double);
double pop(void);
/* kalkulator z odwrotn notacj polsk */
main()
{
int type;
double op2;
char s[MAXOP];
while ((type = getop(s)) != EOF) {
switch (type) {
case NUMBER:
push(atof(s));
break;
case '+':
push(pop() + pop());
break;
case '*':
push(pop() * pop());
break;
case '-':
op2 = pop();
push(pop() - op2);
break;
case '/':
op2 = pop();
if (op2 != 0.0)
push(pop() / op2);
else
printf("error: zero divisor\n");
break;
case '%':
op2 = pop();
if (op2 != 0.0)
push(fmod(pop(), op2));
else
printf("error: zero divisor\n");
60
Rozdzia 4. " Funkcje i struktura programu
break;
case '\n':
printf("\t%.8g\n", pop());
break;
default:
printf("error: unknown command %s\n", s);
break;
}
}
return 0;
}
Zmienili my program g ówny i funkcj getop. Funkcje push i pop pozostaj niezmienione.
Operator modulo (%) jest traktowany podobnie jak operator dzielenia (/). Funkcja bi-
blioteczna fmod oblicza reszt z dzielenia dwóch elementów na wierzcho ku stosu. op2 to
element pobrany z wierzcho ka jako pierwszy.
Oto zmodyfikowana wersja funkcji getop:
#include
#include
#include
#define NUMBER '0' /* sygna , e zosta a znaleziona liczba */
int getch(void);
void ungetch(int);
/* getop: pobiera nast pny operator lub operand (liczb ) */
int getop(char s[])
{
int c, i;
while ((s[0] = c = getch()) == ' ' || c == '\t')
;
s[1] = '\0';
i = 0;
if (!isdigit(c) && c != '.' && c != '-')
return c; /* nie jest liczb */
if (c == '-')
if (isdigit(c = getch()) || c == '.')
s[++i] = c; /* liczba ujemna */
else {
if (c != EOF)
ungetch(c);
return '-'; /* znak minus */
}
if (isdigit(c)) /* pobierz cz ca kowit */
while (isdigit(s[++i] = c = getch()))
;
61
J zyk ANSI C. Programowanie. wiczenia
if (c == '.') /* pobierz cz u amkow */
while (isdigit(s[++i] = c = getch()))
;
s[i] = '\0';
if (c != EOF)
ungetch(c);
return NUMBER;
}
Funkcja getop sprawdza nast pny znak po znaku -, aby okre li , czy dane zawieraj liczb
ujemn . Przyk adowo
- 1
to znak minus i liczba. Jednak
-1.23
jest liczb ujemn .
Rozbudowany kalkulator zapewnia obs ug sekwencji
1 -1 +
-10 3 %
Pierwsze wyra enie prowadzi do uzyskania warto ci 0 (1 + (-1)). Drugie wyra enie ma
warto  1.
wiczenie 4.4 (str. 97)
Utwórz polecenie wypisuj ce element na wierzcho ku stosu bez jego usuwania ze stosu,
polecenie duplikuj ce element na wierzcho ku stosu, polecenie zamieniaj ce miejscami
dwa górne elementy oraz polecenie usuwaj ce ca zawarto stosu.
#include
#include /* dla atof() */
#define MAXOP 100 /* dopuszczalny rozmiar operandu lub operatora */
#define NUMBER '0' /* sygna , e pobrano liczb */
int getop(char []);
void push(double);
double pop(void);
void clear(void);
/* kalkulator z odwrotn notacj polsk */
main()
{
int type;
double op1, op2;
char s[MAXOP];
62
Rozdzia 4. " Funkcje i struktura programu
while ((type = getop(s)) != EOF) {
switch (type) {
case NUMBER:
push(atof(s));
break;
case '+':
push(pop() + pop());
break;
case '*':
push(pop() * pop());
break;
case '-':
op2 = pop();
push(pop() - op2);
break;
case '/':
op2 = pop();
if (op2 != 0.0)
push(pop() / op2);
else
printf("error: zero divisor\n");
break;
case '?': /* wypisz element z wierzcho ka stosu */
op2 = pop();
printf("\t%.8g\n", op2);
push(op2);
break;
case 'c': /* opró nij stos */
clear();
break;
case 'd': /* duplikuj element na wierzcho ku stosu */
op2 = pop();
push(op2);
push(op2);
break;
case 's': /* zamie dwa elementy na wierzcho ku */
op1 = pop();
op2 = pop();
push(op1);
push(op2);
break;
case '\n':
printf("\t%.8g\n", pop());
break;
default:
printf("error: unknown command %s\n", s);
break;
}
}
return 0;
}
63
J zyk ANSI C. Programowanie. wiczenia
Znak nowego wiersza powoduje pobranie elementu z wierzcho ka stosu i wypisanie go.
Dodali my nowy operator, '?', który pobiera element z wierzcho ka stosu, wypisuje
go, a nast pnie zwraca na stos. Nie usuwamy elementu z wierzcho ka stosu w sposób
trwa y (tak jak w przypadku u ycia znaku nowego wiersza), a stosujemy sekwencj
pop-printf-push. Dzi ki temu g ówny program nie musi wiedzie o stosie ani wyko-
rzystywanych do jego obs ugi zmiennych.
Duplikowanie elementu na wierzcho ku stosu polega na zdj ciu go ze stosu i dwukrot-
nym wywo aniu funkcji umieszczaj cej go na stosie ponownie.
Podobnie zamiana miejscami dwóch elementów na wierzcho ku jest realizowana po-
przez zdj cie ich ze stosu i ponowne zapisanie na stosie.
Czyszczenie stosu jest prost operacj , sprowadzaj c si do przypisania zmiennej sp
warto ci 0. Dodali my now funkcj , uzupe niaj c push i pop, która wykonuje w a nie
tak operacj . Pozwala to zachowa zasad , e tylko funkcje operuj ce danymi stosu
odwo uj si do niego i zwi zanych z nim zmiennych.
/* clear: opró nia stos */
void clear(void)
{
sp = 0;
}
wiczenie 4.5 (str. 97)
Dodaj dost p do funkcji biblioteki, takich jak sin, exp, i pow. Patrz w cz ci 4.
dodatku B.
#include
#include
#include /* dla atof() */
#define MAXOP 100 /* dopuszczalny rozmiar operandu lub operatora */
#define NUMBER '0' /* sygna , e pobrano liczb */
#define NAME 'n' /* sygna , e pobrano nazw */
int getop(char []);
void push(double);
double pop(void);
void mathfnc(char []);
/* kalkulator z odwrotn notacj polsk */
main()
{
int type;
double op2;
char s[MAXOP];
while ((type = getop(s)) != EOF) {
64
Rozdzia 4. " Funkcje i struktura programu
switch (type) {
case NUMBER:
push(atof(s));
break;
case NAME:
mathfnc(s);
break;
case '+':
push(pop() + pop());
break;
case '*':
push(pop() * pop());
break;
case '-':
op2 = pop();
push(pop() - op2);
break;
case '/':
op2 = pop();
if (op2 != 0.0)
push(pop() / op2);
else
printf("error: zero divisor\n");
break;
case '\n':
printf("\t%.8g\n", pop());
break;
default:
printf("error: unknown command %s\n", s);
break;
}
}
return 0;
}
/* mathfnc: sprawdza, czy ci g s jest nazw obs ugiwanej funkcji matematycznej */
void mathfnc(char s[])
{
double op2;
if (strcmp(s, "sin") == 0)
push(sin(pop()));
else if (strcmp(s, "cos") == 0)
push(cos(pop()));
else if (strcmp(s, "exp") == 0)
push(exp(pop()));
else if (strcmp(s, "pow") == 0)
op2 = pop();
push(pow(pop(), op2));
} else
printf("error: %s not supported\n", s);
}
65
J zyk ANSI C. Programowanie. wiczenia
Plik ród owy zmodyfikowanej funkcji getop:
#include
#include
#include
#define NUMBER '0' /* sygna , e zosta a znaleziona liczba */
#define NAME 'n' /* sygna , e pobrano nazw */
int getch(void);
void ungetch(int);
/* getop: pobiera nast pny operator, operand lub nazw funkcji */
int getop(char s[])
{
int c, i;
while ((s[0] = c = getch()) == ' ' || c == '\t')
;
s[1] = '\0';
i = 0;
if (islower(c)) /* polecenie lub nazwa */
while (islower(s[++i] = c = getch()))
;
s[i] = '\0';
if (c != EOF)
ungetch(c); /* pobrany o jeden znak za du o */
if (strlen(s) > 1)
return NAME; /* >1 znak, czyli nazwa */
else
return c; /* to mo e by polecenie */
}
if (!isdigit(c) && c != '.')
return c; /* nie jest liczb */
if (isdigit(c)) /* pobierz cz ca kowit */
while (isdigit(s[++i] = c = getch()))
;
if (c == '.') /* pobierz cz u amkow */
while (isdigit(s[++i] = c = getch()))
;
s[i] = '\0';
if (c != EOF)
ungetch(c);
return NUMBER;
}
Zmodyfikowali my funkcj getop, tak aby mog a pobiera ci g ma ych liter i zwraca go
jako typ NAME. Program g ówny rozpoznaje NAME jako jeden z poprawnych typów i wy-
wo uje funkcj mathfnc.
66
Rozdzia 4. " Funkcje i struktura programu
Funkcja mathfnc jest nowym elementem. Wykonuje ona seri instrukcji if a do znale-
zienia nazwy funkcji zapisanej w ci gu s. Je eli nazwa nie zostanie znaleziona, funkcja
zg asza b d. Je eli ci g s jest nazw jednej z obs ugiwanych funkcji matematycznych,
mathfnc pobiera ze stosu odpowiedni liczb elementów i wywo uje t funkcj . Funkcja
zwraca warto , któr mathfnc umieszcza na stosie.
Przyk adowo funkcja sin oczekuje argumentu w radianach, a sinus PI / 2 ma warto 1.
3.14159265 2 / sin
Pierwsza operacja to dzielenie PI przez 2. Wynik zostaje umieszczony na stosie.
Funkcja sin pobiera t warto z wierzcho ka stosu, oblicza warto ci sinus i zapisuje
wynik, 1, ponownie na stosie.
3.14159265 2 / sin 0 cos +
daje wynik 2, poniewa sinus PI / 2 to 1 i cosinus zera to 1.
Inny przyk ad,
5 2 pow 4 2 pow +
podnosi 5 do pot gi 2, nast pnie 4 do pot gi 2, po czym dodaje te dwie warto ci.
Funkcja getop nie zna nazw funkcji matematycznych. Zwraca ona jedynie znalezione
ci gi. Zapewnia to mo liwo atwego rozbudowywania funkcji mathfnc i wprowadzania
obs ugi dalszych operacji.
wiczenie 4.6 (str. 98)
Dodaj polecenia obs ugi zmiennych ( atwo jest zapewni mo liwo korzystania z dwu-
dziestu sze ciu zmiennych przy u yciu jednoliterowych nazw). Dodaj zmienn prze-
chowuj c ostatni wypisan warto .
#include
#include /* dla atof() */
#define MAXOP 100 /* dopuszczalny rozmiar operandu lub operatora */
#define NUMBER '0' /* sygna , e pobrano liczb */
int getop(char []);
void push(double);
double pop(void);
/* kalkulator z odwrotn notacj polsk */
main()
{
int i, type, var = 0;
double op2, v;
char s[MAXOP];
67
J zyk ANSI C. Programowanie. wiczenia
double variable[26];
for (i = 0; i < 26; i++)
variable[i] = 0.0;
while ((type = getop(s)) != EOF) {
switch (type) {
case NUMBER:
push(atof(s));
break;
case '+':
push(pop() + pop());
break;
case '*':
push(pop() * pop());
break;
case '-':
op2 = pop();
push(pop() - op2);
break;
case '/':
op2 = pop();
if (op2 != 0.0)
push(pop() / op2);
else
printf("error: zero divisor\n");
break;
case '=':
pop();
if (var >= 'A' && var <= 'Z')
variable[var = 'A'] = pop();
else
printf("error: no variable name\n");
break;
case '\n':
v = pop();
printf("\t%.8g\n", v);
break;
default:
if (type >= 'A' && type <= 'Z')
push(variable[type  'A']);
else if (type == 'v')
push(v);
else
printf("error: unknown command %s\n", s);
break;
}
var = type;
}
return 0;
}
68
Rozdzia 4. " Funkcje i struktura programu
Dodane zmienne to wielkie litery, od A do Z. Litery te s u zarazem jako indeksy zmien-
nej tablicowej. Wprowadzili my tak e zmienn v, w której zapisywana jest ostatnia
wypisywana warto .
Gdy program napotyka nazw zmiennej (od A do Z lub v), zapisuje jej warto na stosie.
Dost pny jest tak e nowy operator, '=', który przypisuje element z wierzcho ka stosu
zmiennej poprzedzaj cej operator. Na przyk ad
3 A =
przypisuje warto 3 zmiennej A. Pó niejsze
2 A +
dodaje liczby 2 i 3 (warto zmiennej A). Po doj ciu do znaku nowego wiersza program
wypisuje liczb 5 i przypisuje jednocze nie warto 5 zmiennej v. Je eli nast pn
operacj jest
v 1 +
to wynikiem jest 6: 5+1.
wiczenie 4.7 (str. 98)
Napisz procedur ungets(s), która zwraca do danych wej ciowych ca y ci g znaków. Czy
funkcja ta powinna korzysta ze zmiennych buf i bufp, czy raczej tylko z funkcji ungetch?
#include
/* ungets: zwraca ci g znaków do strumienia danych wej ciowych */
void ungets(char s[])
{
int len = strlen(s);
void ungetch(int);
while (len > 0)
ungetch(s[--len]);
}
Zmienna len zawiera liczb znaków w ci gu s (bez ko cowego '\0'), która jest okre-
lana przy u yciu funkcji strlen (patrz podrozdzia 2.3 podr cznika K&R).
Funkcja ungets wywo uje procedur ungetch (patrz koniec podrozdzia u 4.3 podr cz-
nika K&R) len razy, za ka dym razem przekazuj c do strumienia danych wej ciowych
jeden znak ci gu s. Funkcja dba o przekazywanie znaków w odwróconej kolejno ci.
Funkcja ungets nie musi zna zmiennych buf i bufp. Procedura ungetch zapewnia wy-
krywanie b dów i w a ciw prac z tymi zmiennymi.
69
J zyk ANSI C. Programowanie. wiczenia
wiczenie 4.8 (str. 98)
Zmodyfikuj funkcje getch i ungetch, przyj wszy za o enie, e nigdy nie b dzie wycofy-
wany wi cej ni jeden znak.
#include
char buf = 0;
/* getch: pobiera znak danych wej ciowych (móg by wcze niej wycofany przez ungetch */
int getch(void)
{
int c;
if (buf != 0)
c = buf;
else
c = getchar();
buf = 0;
return c;
}
/* ungetch: wycofuje znak do strumienia danych wej ciowych */
void ungetch(int c)
{
if (buf != 0)
printf("ungetch: too many characters\n");
else
buf = c;
}
Bufor, buf, nie jest ju tablic , poniewa nigdy nie b dzie w nim przechowywany
wi cej ni jeden znak.
Zmienna buf jest inicjalizowana przy adowaniu programu warto ci 0. Funkcja getch
przywraca t warto za ka dym razem, gdy pobiera znak. Funkcja ungetch przed za-
pisaniem znaku sprawdza, czy bufor jest pusty. Je eli bufor nie jest pusty, wy wietla
komunikat b du.
wiczenie 4.9 (str. 98)
Nasze funkcje getch i ungetch nie obs uguj poprawnie wycofywania znaku EOF. Za-
stanów si , jakie powinny one mie cechy w przypadku cofania znaku EOF, po czym
zaimplementuj now koncepcj .
#include
#define BUFSIZE 100
70
Rozdzia 4. " Funkcje i struktura programu
int buf[BUFSIZE]; /* bufor dla ungetch */
int bufp = 0; /* nast pna wolna pozycja w buforze */
/* getch: pobiera znak danych wej ciowych (móg by wcze niej wycofany przez ungetch */
int getch(void)
{
return (bufp > 0) ? buf[--bufp] : getchar();
}
/* ungetch: wycofuje znak do strumienia danych wej ciowych */
void ungetch(int c)
{
if (bufp >= BUFSIZE)
printf("ungetch: too many characters\n");
else
buf[bufp++] = c;
}
W funkcjach getch i ungetch przedstawionych w podr czniku K&R bufor, buf, jest dekla-
rowany jako tablica znaków:
char buf[BUFSIZE];
J zyk C nie wymaga, aby zmienna char zosta a okre lona jako signed lub unsigned
(patrz podrozdzia 2.7 podr cznika K&R). Konwersja warto ci char na int nie mo e
prowadzi do uzyskania warto ci ujemnej. Na niektórych komputerach, gdy lewy skrajny
bit warto ci char jest równy 1, konwersja na int prowadzi do uzyskania warto ci ujem-
nej. Na innych konwersja polega na dodaniu z lewej strony odpowiedniej liczby zer.
Takie przekszta cenie zawsze prowadzi do uzyskania liczby dodatniej, niezale nie od
tego, czy lewy skrajny bit mia warto 1, czy nie.
W notacji szesnastkowej  1 to 0xFFFF (w przypadku 16 bitów). Po zapisaniu warto ci
0xFFFF w zmiennej char zmienna zawiera 0xFF. Konwersja 0xFF na int mo e prowadzi
do uzyskania warto ci 0x00FF, czyli 255, lub 0xFFFF, czyli  1.
liczba ujemna ( 1) -> znak -> liczba int
0xFFFF 0xFF 0x00FF (255)
0xFFFF 0xFF 0xFFFF ( 1)
Je eli mamy traktowa EOF ( 1) jak ka dy inny znak, zmienna buf powinna zosta za-
deklarowana jako tablica liczb ca kowitych:
int buf[BUFSIZE];
Nie s wtedy wykonywane adne operacje konwersji i warto EOF ( 1), podobnie jak
ka da liczba ujemna, jest obs ugiwana w sposób jednolity na wszystkich platformach.
71
J zyk ANSI C. Programowanie. wiczenia
wiczenie 4.10 (str. 98)
Alternatywna organizacja pracy z danymi wej ciowymi opiera si na u yciu getline
w celu pobrania ca ego wiersza. Dzi ki temu funkcje getch i ungetch nie s potrzebne.
Przekszta kalkulator, tak aby jego praca opiera a si na takim podej ciu do danych
wej ciowych.
#include
#include
#define MAXLINE 100
#define NUMBER '0' /* sygna , e zosta a znaleziona liczba */
int getline(char line[], int limit);
int li = 0; /* indeks wiersza wej ciowego */
char line [MAXLINE]; /* jeden wiersz wej ciowy */
/* getop: pobiera nast pny operator lub operand (liczb ) */
int getop(char s[])
{
int c, i;
if (line[li] == '\0')
if (getline(line, MAXLINE) == 0)
return EOF;
else
li = 0;
while ((s[0] = c = line[li++]) == ' ' || c == '\t')
;
s[1] = '\0';
if (!isdigit(c) && c != '.')
return c; /* nie jest liczb */
i = 0;
if (isdigit(c)) /* pobierz cz ca kowit */
while (isdigit(s[++i] = c = line[li++]))
;
if (c == '.') /* pobierz cz u amkow */
while (isdigit(s[++i] = c = line[li++]))
;
s[i] = '\0';
li--;
return NUMBER;
}
Zamiast getch i ungetch u ywamy w getop funkcji getline. line to tablica zawieraj ca
jeden pe ny wiersz danych wej ciowych. li to indeks kolejnego znaku w line. Deklaru-
jemy line i li jako zmienne wewn trzne, aby zachowywa y warto ci mi dzy wywo aniami.
72
Rozdzia 4. " Funkcje i struktura programu
Gdy getop dochodzi do ko ca wiersza (lub aden wiersz nie zosta jeszcze pobrany),
if (line[li] == '\0')
nast puje wywo anie getline w celu pobrania nowego wiersza danych.
W oryginalnej wersji (podrozdzia 4.3 podr cznika K&R) funkcja getop wywo uje getch
za ka dym razem, gdy potrzebny jest nowy znak. W tej wersji pobierany jest znak na
pozycji li w tablicy line, po czym warto li jest zwi kszana. Na ko cu funkcji, zamiast
wywo ywa ungetch w celu zwrócenia znaku do strumienia danych wej ciowych, zmniej-
szamy li, aby wycofa si o jeden znak.
Warto pami ta , e ka da funkcja ma mo liwo wykorzystywania i modyfikowania
zmiennych zewn trznych stosowanych w innych funkcjach, wi c zmienne li i line
mog zosta zmienione przez funkcj inn ni getop. Czasem wskazane jest zabez-
pieczenie programu przed takimi sytuacjami. Umo liwia to zadeklarowanie zmien-
nych jako static. Nie zrobili my tego, bo zmienne static zostan omówione dopiero
w podrozdziale 4.6 podr cznika K&R.
wiczenie 4.11 (str. 102)
Zmodyfikuj funkcj getop w taki sposób, aby nie korzysta a z funkcji ungetch. Wska-
zówka: u yj wewn trznej zmiennej statycznej.
#include
#include
#define NUMBER '0' /* sygna , e zosta a znaleziona liczba */
int getch(void);
/* getop: pobiera nast pny operator lub operand (liczb ) */
int getop(char s[])
{
int c, i;
static int lastc = 0;
if (lastc == 0)
c = getch();
else {
c = lastc;
lastc = 0;
}
while ((s[0] = c) == ' ' || c == '\t')
c = getch();
s[1] = '\0';
if (!isdigit(c) && c != '.')
return c; /* nie jest liczb */
i = 0;
73
J zyk ANSI C. Programowanie. wiczenia
if (isdigit(c)) /* pobierz cz ca kowit */
while (isdigit(s[++i] = c = getch()))
;
if (c == '.') /* pobierz cz u amkow */
while (isdigit(s[++i] = c = getch()))
;
s[i] = '\0';
if (c != EOF)
lastc = c;
return NUMBER;
}
Zmodyfikowali my funkcj getop, tak aby korzysta a ze zmiennej static, która pa-
mi ta ostatni znak, który powinien zosta wycofany do strumienia danych wej ciowych.
Poniewa nie korzystamy z funkcji ungetch, zapisujemy ten znak w zmiennej lastc.
Wywo anie funkcji getop prowadzi do sprawdzenia, czy lastc zawiera wycofany znak.
Je eli nie, nast puje wywo anie getch w celu pobrania nowego znaku. Je eli lastc zawiera
wycofany znak, to funkcja getop kopiuje go do zmiennej c i zeruje warto lastc. Pierw-
sza instrukcja while uleg a pewnym zmianom. Wynikaj one z tego, e getop musi
pobiera nowy znak tylko po zako czeniu przetwarzania bie cego znaku w c.
wiczenie 4.12 (str. 107)
Zaadaptuj koncepcj funkcji printd do napisania rekurencyjnej wersji funkcji itoa.
Innymi s owy, przekszta liczb ca kowit na ci g znaków, wywo uj c procedur re-
kurencyjn .
#include
/* itoa: konwertuje liczb n na ci g znaków s; wersja rekurencyjna */
void itoa(int n, char s[])
{
static int i;
if (n / 10)
itoa(n / 10, s);
else {
i = 0;
if (n < 0)
s[i++] = '-';
}
s[i++] = abs(n) % 10 + '0';
s[i] = '\0';
}
74
Rozdzia 4. " Funkcje i struktura programu
Funkcja itoa pobiera dwa argumenty: liczb ca kowit n i tablic znaków s. Je eli wynik
dzielenia ca kowitego n/10 jest ró ny od zera, funkcja wywo uje sam siebie, przekazuj c
jako argument n/10:
if (n / 10)
itoa(n / 10, s);
Gdy w jednym z kolejnych wywo a rekurencyjnych n/10 ma warto 0, mamy do czy-
nienia z najbardziej znacz c cyfr n. Statyczna zmienna i jest indeksem tablicy s. Je eli
liczba n jest ujemna, umieszczamy znak minus na pierwszej pozycji tablicy i zwi k-
szamy i. Gdy itoa powraca z kolejnych wywo a rekurencyjnych, obliczane s kolejne
cyfry, od lewej do prawej strony. Zwró my uwag , e na ka dym poziomie zostaje dodane
'\0' ko cz ce ci g, które na kolejnym poziomie zostaje zast pione nast pn cyfr liczby.
Wyj tkiem jest jedynie zako czenie ostatniego wywo ania itoa, po którym zapisany
znacznik ko ca ci gu pozostaje w tablicy znaków.
wiczenie 4.13 (str. 107)
Napisz rekurencyjn wersj funkcji reverse(s), odwracaj cej  w miejscu ci g znaków s.
#include
/* reverse: odwraca w miejscu ci g s */
void reverse(char s[])
{
void reverser(char s[], int i, int len);
reverser(s, 0, strlen(s));
}
/* reverser: odwraca w miejscu ci g s; algorytm rekurencyjny */
void reverser(char s[], int i, int len)
{
int c, j;
j = len  (i + 1);
if (i < j) {
c = s[i];
s[i] = s[j];
s[j] = c;
reverser(s, ++i, len);
}
}
Musimy zachowa ten sam interfejs procedury reverse niezale nie od implementacji.
Oznacza to, e mo emy przekaza do niej tylko ci g znaków.
Funkcja reverse okre la d ugo ci gu i wywo uje funkcj reverser, która wykonuje
w a ciw operacj odwrócenia ci gu s w miejscu.
75
J zyk ANSI C. Programowanie. wiczenia
Funkcja reverser pobiera trzy argumenty: s jest odwracanym ci giem, i to indeks ci gu
(od lewej), a len to d ugo ci gu (strlen(s); patrz podrozdzia 2.3 podr cznika K&R).
Pocz tkowo parametr i ma warto 0. j to indeks ci gu, wskazuj cy pozycj wzgl dem
prawego ko ca tego ci gu. Warto j jest obliczana jako
j = len  (i + 1);
Znaki ci gu s zamieniane miejscami, pocz wszy od skrajnych, a do rodka ci gu
 najpierw zamieniane s s[0] i s[len-1], potem s[1] i s[len-2] itd. Warto indeksu
i jest zwi kszana o jeden przed ka dym kolejnym wywo aniem funkcji reverser:
reverser(s, ++i, len);
Zamienianie znaków jest kontynuowane do momentu, gdy dwa indeksy wskazuj ten sam
znak (i == j) albo indeks liczony od lewej strony wskazuje znak na prawo od znaku
wskazywanego przez indeks liczony od lewej strony (i > j).
Nie jest to korzystne zastosowanie rekurencji. Pewne problemy dobrze poddaj si
rozwi zaniom rekurencyjnym  przyk adem mo e by funkcja treeprint przedstawiona
w podrozdziale 6.5 podr cznika K&R. Inne lepiej rozwi zywa innymi sposobami.
Do tej ostatniej kategorii nale y problem odwracania ci gu.
wiczenie 4.14 (str. 110)
Zdefiniuj makro swap(t,x,y) wymieniaj ce warto ci dwóch argumentów, których typ
to t (pomocna b dzie struktura blokowa).
#define swap(t, x, y) { t _z; \
_z = y; \
y = x; \
x = _z; }
U ywaj c nawiasów klamrowych, definiujemy blok. Na pocz tku bloku mo emy za-
deklarowa zmienne lokalne. _z to zmienna lokalna typu t, która pomaga zamieni
dwa argumenty.
Makro swap dzia a poprawnie, o ile aden z argumentów nie ma nazwy _z. Je eli tak jest,
swap(int, _z, x);
to po rozwini ciu makra uzyskujemy
{ int _z; _z = _z; _z = x; x = _z; }
i wymiana nie nast puje. Przyjmujemy wi c za o enie, e _z nie b dzie wykorzysty-
wane jako nazwa zmiennej.
76


Wyszukiwarka

Podobne podstrony:
Jezyk ANSI C Programowanie Wydanie II jansic
C cwiczenia Wydanie II
Programowanie w jezyku C cwiczenia praktyczne Wydanie II cwprc2
GIMP cwiczenia praktyczne Wydanie II
C cwiczenia praktyczne Wydanie II
Java?ektywne programowanie Wydanie II javep2
JavaScript cwiczenia praktyczne Wydanie II cwjas2

więcej podobnych podstron