4 FUNKCJE I STRUKTURA PROGRAMU_________._—----
W bibliotece standardowej występuje funkcja ungetc, która przechowuje tylko jeden
zwrócony na wejście znak; omówimy ją w rozdz. 7. Tu do przechowywania zwróconych znaków zastosowaliśmy tablicę znaków, a nie jeden znak, aby zilustrować ogólniejsze podejście do zagadnienia.
Ćwiczenie 4.3. Posługując się podstawowym schematem, łatwo można rozszerzyć funkcje kalkulatora. Dodaj więc obsługę operatora dzielenia modulo % oraz możliwość wprowadzania liczb ujemnych.
*
Ćwiczenie 4.4. Wprowadź polecenia; wypisywania liczby z wierzchołka stosu bez zdejmowania jej oraz zamiany miejscami dwóch szczytowych elementów stosu. Dodaj też polecenie czyszczące stos.
Ćwiczenie 4.5. Zorganizuj dostęp do bibliotecznych funkcji numerycznych, jak sin, exp czy pow. Zajrzyj do opisu nagłówka <math.h> w dodatku B4.
Ćwiczenie 4.6. Dodaj polecenia pozwalające używać zmiennych. (Łatwo to zrobić dla dwudziestu sześciu zmiennych o nazwach jednoliterowych.) Wprowadź obsługę zmiennej, w której pamięta się wartość ostatnio wypisanej liczby.
Ćwiczenie 4.7. Napisz funkcję ungets(s), która będzie oddawać na wejście cały tekst z argumentu s. Czy funkcja ta powinna coś wiedzieć o obiektach buf i bufp, czy też po prostu można w niej skorzystać z funkcji ungetch?
Ćwiczenie 4.8. Przypuśćmy, że nigdy nie zajdzie potrzeba oddawania na wejście więcej niż jednego znaku. Zgodnie z tym założeniem zmień odpowiednio funkcje getch i ungetch.
*
Ćwiczenie 4.9. Nasze funkcje getch i ungetch nie obsługują poprawnie znacznika końca pliku EOF. Określ, jakie właściwości powinny mieć te funkcje, żeby EOF mógł być oddawany, a następnie zrealizuj swój projekt.
*
Ćwiczenie 4.10. Przy innej organizacji program mógłby korzystać z funkcji getline, która czyta z wejścia cały wiersz; wówczas funkcje getch i ungetch w ogóle nie byłyby potrzebne. Rozpatrz na nowo program kalkulatora z zastosowaniem tego podćjścia.
Funkcje i zmienne zewnętrzne, z których składa się program w języku C, nie musz4 być tłumaczone wszystkie naraz. Tekst źródłowy programu można umieścić w wielu
4.4 ZASIĘG NAZW _______
plikach, a uprzednio przetłumaczone kawałki mogą być dołączane resują nas odpowiedzi na cztery pytania:
• Jak powinny być zadeklarowane zmienne, aby tłumaczenie było poprawne?
• Jak należy rozmieścić deklaracje w tekście programu, aby wszystkie fragmenty zostały poprawnie połączone podczas jego ładowania?
• Jak poprawnie zorganizować deklaracje, aby dla każdej z nich występowała tylko jedna kopia?
• Jak zmiennym zewnętrznym nadać ich wartości początkowe?
W celu omówienia tych kwestii zreorganizujemy program kalkulatora tak, aby mieścił się w kilku plikach źródłowych. Z praktycznego punktu widzenia program ten jest zbyt mały, żeby zasługiwał na podział, ale jest dobrym przykładem tych problemów, które mogą pojawić się w większych programach.
Zasięgiem nazwy jest ta część programu, wewnątrz której można daną nazwę stosować. Dla zmiennej automatycznej deklarowanej na początku funkcji zasięgiem jest cała funkcja zawierająca deklarację zmiennej. Zmienne lokalne o tej samej nazwie, które występują w różnych funkcjach, nie są ze sobą związane w żaden sposób. To samo dotyczy parametrów funkcji, które w rzeczywistości są zmiennymi lokalnymi.
Zasięg zmiennej zewnętrznej i funkcji rozciąga się od miejsca, w którym została ona zadeklarowana w pliku źródłowym podlegającym kompilacji, do końca tego pliku. Na przykład, jeśli main, sp, val, push i pop są zdefiniowane w jednym pliku w następującej kolejności:
main() { ... } int sp = 0;
double val[MAXVALJ;
void push(double f) { ... } double pop(void) { ... }
to funkcje push i pop mogą odwoływać się do zmiennych sp i val po prostu przez nazwy; żadne inne deklaracje nie są potrzebne. Ale nazwy tych zmiennych nie są widoczne dla funkcji main, tak samo zresztą, jak obie funkcje push i pop.
Z drugiej strony, jeśli odwołanie do zmiennej zewnętrznej występuje przed jej definicją lub jeśli jej definicja znajduje się w innym pliku źródłowym niż odwołanie, to deklaracja extern jest obowiązkowa.
115