4 FUNKCJE I STRUKTURA PROGRAMU
rozwagą, ponieważ mogą niekorzystnie wpływać na strukturę programu, a także przyczyniać się do powstawania programów ze zbyt wieloma powiązaniami między funkcjami przez dane.
Zmienne zewnętrzne są także użyteczne ze względu na ich zasięg oraz okres ich istnienia. Zmienne automatyczne są dla funkcji wewnętrzne; zaczynają istnieć w chwili wywołania funkcji i nikną po jej zakończeniu. Natomiast zmienne zewnętrzne istnieją stale. Nie pojawiają się i nie znikają, a więc zachowują swoje wartości między jednym a drugim wywołaniem funkcji. Jeśli dwie funkcje muszą mieć kilka wspólnych danych i żadna z nich nie wywołuje drugiej, to często najwygodniej jest trzymać te dane w zmiennych zewnętrznych, zamiast przekazywać je w argumentach.
Bardziej szczegółowo omówimy ten temat na większym przykładzie: napiszemy inny program kalkulatora, który dopuszcza operatory +, -, * oraz /. Zamiast notacji wrost-kowej program będzie oparty na Odwrotnej Notacji Polskiej, ponieważ jest ona łatwiejsza do realizacji. (Odwrotna Notacja Polska jest stosowana w wielu kalkulatorach kieszonkowych, a także w takich językach, jak Forth czy Postscript.)
W Odwrotnej Notacji Polskiej każdy operator następuje po swoich argumentach; nć przykład wyrażenie wrostkowe
(1 - 2) * (4 + 5)
jest wprowadzane w postaci
1 2-45 + *
Nawiasy nie są potrzebne; notacja jest jednoznaczna dopóty, dopóki wiemy, ilu argu mentów spodziewa się każdy operator.
Realizacja tego zadania jest prosta. Każdy argument jest wstawiany na stos. Gd) pojawia się operator, wówczas ze stosu zdejmuje się odpowiednią liczbę argumentów (dwa dla operatorów dwuargumentowych), na nich wykonuje się obliczenie wskazań; przez operator, a następnie wynik zapamiętuje z powrotem na stosie. Wyjaśnimy to na ostatnim przykładzie: argumenty 1 i 2 zapamiętujemy na stosie, następnie zastępujemy je przez ich różnicę, tj. -1. Z kolei wstawiamy na stos 4 i 5, i zaraz poteir zastępujemy je przez ich sumę 9. Operator mnożenia stosujemy do argumentów i 9, po czym na stosie zastępujemy je ich iloczynem -9. Po osiągnięciu końca danyd wejściowych zdejmujemy ze stosu wartość uzyskaną na jego szczycie i wypisujemy ją na wyjście.
Struktura programu jest więc pętlą, w której z chwilą pojawienia się każdego operatora lub argumentu wykonuje się odpowiednią dla niego operację:
4.3 ZMIENNE ZEWNĘTRZNE______
_pow/?-Cfid by
Mi si ol
while (następny operator lub argument nie jest znacznikiem końca if (liczba)
zapamiętaj jq na stosie else if (ioperator)
weź argumenty ze stosu wykonaj obliczenie zapamiętaj wynik na stosie else błąd
Operacje wstawiania na stos i zdejmowania ze stosu są banalne. Ponieważ jednak uzupełniono je wykrywaniem i sygnalizacją błędów, są one na tyle długie, że lepiej napisać dla nich osobne funkcje, zamiast powtarzać stale te same instrukcje. W programie powinna także wystąpić osobna funkcja pobierająca z wejścia następny operator lub argument.
Dotychczas nie rozważyliśmy jeszcze głównego założenia projektowego dotyczącego stosu - gdzie on jest, to znaczy jakie funkcje będą mieć do niego bezpośredni dostęp. Jedną z możliwości jest umieszczenie go w funkcji main i przekazywanie go oraz bieżącej pozycji jego wierzchołka do funkcji realizujących obsługę stosu. Ale funkcja main nie potrzebuje informacji o zmiennych sterujących operacjami na stosie; ona wykonuje jedynie operacje „wstaw” i „zdejmij”. Podjęliśmy więc decyzję, aby stos i związane z nim informacje były przechowywane w zmiennych zewnętrznych, dostępnych dla funkcji obsługi stosu push (wstaw) i pop (zdejmij), lecz niedostępnych dla funkcji main.
Napisanie programu dla podanego schematu jest dość proste. Jeśli teraz wyobrazimy sobie, że cały program jest zawarty w jednym pliku źródłowym, to będzie on wyglądał tak:
#include.y /* pliki nagłówkowe */
#define.ę /* definicje stałych symbolicznych */
deklaracje funkcji wywoływanych w main
$
zmienne zewnętrzne używane przez funkcje push i pop
void push(double f) { ... } double pop(void) { ... }
int getop(char s[ ]) { ... }
procedury wołane przez getop
Później omówimy zagadnienie podziału tego pliku na dwa lub więcej plików źródłowych.
109