Turbo Pascal. Programowanie

Turbo Pascal.
Programowanie

Autor: Tomasz M. Sadowski
Format: B5, stron: 136

Copyright © 1996 by Wydawnictwo Helion
wersja spakowana

helion.pl

Typy i stałe

Zajmiemy się obecnie dwoma spokrewnionymi ze sobą zagadnieniami: definiowaniem własnych typów oraz stałymi symbolicznymi. Główny obszar zastosowań stałych i typów związany jest z wykorzystaniem typów strukturalnych (dlatego mówimy o nich właśnie teraz); wykorzystanie definicji typów pozwala na ominięcie pewnych ograniczeń w przekazywaniu parametrów (zobacz koniec poprzedniego rozdziału). Poza tym używanie właściwie dobranych definicji typów i stałych polepsza czytelność i estetykę programu oraz zmniejsza ryzyko błędów.

Pokazany na końcu poprzedniego rozdziału przykład ilustruje niezręczną próbę przekazania do procedury parametru typu strukturalnego. Niestety, nasz zapis stanowi próbę przemycenia opisu (definicji) typu w miejscu do tego nie przeznaczonym: na liście parametrów formalnych procedury lub funkcji mogą znajdować się wyłącznie identyfikatory parametrów i odpowiadające im identyfikatory (nie definicje!) typów. Dopóki posługiwaliśmy się parametrami typu prostego, problem nie istniał, gdyż na liście parametrów umieszczałeś po prostu nazwę odpowiedniego typu. W przypadku typów strukturalnych sprawa się komplikuje. Nie dość, że na liście parametrów nie wolno umieścić definicji typu (jak więc zdefiniować parametr strukturalny?), to jeszcze identycznie wyglądające deklaracje tablic

  var
    t1 : array[1..100] of real;
    t2 : array[1..100] of real;

wcale nie tworzą obiektów identycznego typu - każda z nich interpretowana jest jako definicja nowego typu, co utrudnia niektóre operacje (nie można np. dokonać przypisania całej tablicy t2 do t1). Na dobitkę okazuje się, że zgodność typów wymagana jest również w przypadku przekazywania parametrów (co było zresztą łatwe do przewidzenia).

Wyjściem z tego impasu jest zdefiniowanie identyfikatora typu, czyli nazwy będącej synonimem danego typu strukturalnego. Identyfikator taki wykorzystamy następnie do deklarowania zmiennych i parametrów dokładnie tak samo, jak czynilibyśmy to z identyfikatorem typu prostego. Aby zdefiniować identyfikator typu, musisz użyć słowa kluczowego type (typ) w następującej konstrukcji:

 
type
 
  nazwa-typu = opis-typu

(zwróć uwagę, że w definicji typu wykorzystuje się znak równości, a nie dwukropek). Nazwa-typu określa definiowany identyfikator (i podlega wszystkim regułom dotyczącym identyfikatorów), zaś opis-typu jest takim samym opisem, jaki stosowany jest w deklaracjach zmiennych. Różnica polega tylko na tym, że w przypadku deklaracji zmiennej kompilator tworzy odpowiednią strukturę danych i rezerwuje dla niej miejsce w pamięci, natomiast zdefiniowanie typu powoduje jedynie utworzenie szablonu (nie jest tworzony żaden fizyczny obiekt), który będzie wykorzystywany w deklaracjach (dopiero wtedy kompilator będzie rezerwował miejsce na dane).

Przykładowa definicja typu rekordowego opisującego książkę (i deklaracja odpowiedniej tablicy rekordów) będzie więc miała postać

  type
    Ksiazka = record { tak samo, jak w deklaracji zmiennej }
      Tytul : string[30];
      Autor : string[25];
      Wypozyczajacy : string[25];
      Licznik : word);
    end;
   
  var
    Katalog : array[1..750] of Ksiazka;

Już na pierwszy rzut oka wygląda to ładniej (Katalog zawiera Ksiazki, a nie jakieś rekordy, których przeznaczenia należy się dopiero domyślać), no a poza tym możliwe jest wreszcie zadeklarowanie odpowiednich parametrów:

procedure Wprowadz(var r : Ksiazka);

Definiowanie nowych identyfikatorów typów ma zastosowanie głównie w programach posługujących się strukturalnymi reprezentacjami danych, chociaż możliwe jest również definiowanie nowych typów prostych (a nawet przedefiniowywanie typów standardowych). Oto przykład:

  type
    float = extended; { real jeśli nie masz koprocesora }

Powyższa definicja tworzy nowy typ zmiennoprzecinkowy float, tożsamy z typem extended (dającym największą możliwą precyzję i zakres wartości). Jeśli program przeznaczony jest dla komputera pozbawionego koprocesora (i nie ma emulować tego ostatniego), wystarczy w miejsce typu extended wpisać real. Deklarując wszystkie liczby rzeczywiste w programie jako float uzyskasz w ten sposób możliwość łatwej adaptacji programu do warunków sprzętowych.

Zauważ, że w deklaracji zmiennej Katalog wymiar tablicy określony jest jawnie liczbą 750 (wynika ona z podzielenia wielkości dostępnej pamięci przez wielkość rekordu). Jest dość prawdopodobne, że do wartości tej będziesz się w programie odwoływał jeszcze kilkakrotnie (choćby podczas przeszukiwania katalogu lub usuwania jego elementów). Wyobraź sobie teraz, że postanowiłeś dodać do rekordu krótki opis książki w postaci łańcucha 20 znaków. Wpłynie to rzecz jasna na wielkość rekordu, a zatem i na dopuszczalną wielkość tablicy, która zmaleje teraz do około 600 pozycji. Po dokonaniu zmiany będziesz musiał wyszukać w programie wszystkie miejsca, w których występowała liczba 750 i zastąpić ją liczbą 600 - jeśli o którymś zapomnisz, konsekwencje mogą być niezbyt przyjemne (do zawieszenia komputera włącznie). Drugi problem związany z użyciem liczby 750 (lub 600) pojawia się w kilka miesięcy po napisaniu programu i sprowadza się do faktu, że zwykle trudno sobie przypomnieć, co to za 750 i dlaczego akurat tyle.

Eleganckim sposobem na poradzenie sobie z opisanym problemem są stałe symboliczne. Podobnie jak definicja typu pozwala na utworzenie identyfikatora będącego synonimem definicji typu, tak definicja stałej symbolicznej umożliwia utworzenie identyfikatora będącego synonimem określonej wartości stałej (niekoniecznie liczbowej). Do definiowania stałych używane jest słowo kluczowe const (ang. constant - stała), zaś składnia definicji wygląda następująco:

  const
    nawa-stałej = wartość

Każde wystąpienie nazwy-stałej w programie zostanie podczas kompilacji zastąpione wartością. Podobnie jak w przypadku typów, zdefiniowanie stałej nie powoduje zarezerwowania miejsca w pamięci; w związku z tym musisz pamiętać, że stała symboliczna nie jest l-wartością i nie może się w programie znaleźć po lewej stronie operatora przypisania ani w miejscu parametru przekazywanego przez nazwę (pomijając oczywistą herezję, jaką jest próba zmiany wartości stałej).

Definicja stałej określającej maksymalną pojemność tablicy (i stosownie zmodyfikowana deklaracja tablicy rekordów) będzie więc wyglądała następująco:

  const
    POJEMNOSC_KATALOGU = 750;
  var
    Katalog : array[1..POJEMNOSC_KATALOGU] of Ksiazka;

Zauważ, że identyfikator stałej zapisany jest dużymi literami (jest to istotne tylko dla programisty - Turbo Pascal nie rozróżnia dużych liter od małych). Konwencja ta - pozwalająca łatwo odróżnić STAŁE od zmiennych, jest dość powszechnie stosowana przez programistów i warto ją sobie przyswoić.

Jeśli nie chce Ci się przeliczać wartości naszej stałej po każdej zmianie struktury rekordu (czego z drugiej strony nie powinieneś robić zbyt często, bo dowodzi to złego zaprojektowania programu), możesz użyć następującej definicji:

  const
    MAX_PAMIEC = 63000; { maksymalna wielkosc katalogu }
      { w bajtach }
    POJEMNOSC_KATALOGU = MAX_PAMIEC div SizeOf(Ksiazka);

Jak nietrudno się domyślić, raz zdefiniowanej stałej można użyć w kolejnych definicjach. Co jednak znacznie ciekawsze, stała może zostać zdefiniowana za pomocą wyrażenia zawierającego inne stałe i niektóre operatory (wyrażenie musi dać się obliczyć w trakcie kompilacji, nie może więc zawierać identyfikatorów zmiennych i funkcji bibliotecznych; dokładniejsze informacje na ten temat znajdziesz w systemie pomocy). W naszym przypadku wartość stałej zdefiniowana jest jako wynik (całkowity) podzielenia wielkości dostępnej pamięci (zdefiniowanej inną stałą) przez rozmiar rekordu opisującego książkę. W tym momencie możesz przestać przejmować się jakimikolwiek liczbami.

Stosowanie stałych symbolicznych i definicji typów stanowi jedną z podstaw dobrej praktyki programowania. Odpowiednio użyte, mechanizmy te znacznie poprawiają czytelność programu i zmniejszają jego podatność na błędy w trakcie pisania i poprawiania.

Co prawda przed chwilą powiedziano, że stała zdefiniowana słowem kluczowym const nie jest l-wartością, okazuje się jednak, że słowa tego można użyć również do definiowania... zmiennych. Chodzi tu o tzw. zmienne z wartością początkową (zmienne predefiniowane, ang. typed constant). Zmienna taka jest normalną zmienną - tyle, że uzyskuje wartość już w momencie deklaracji (definicji). Składnia definicji zmiennej z wartością początkową jest następująca:

  const
    nazwa-zmiennej : typ = wartość-początkowa

Zmienne predefiniowane pozwalają na łatwe inicjalizowanie danych w programie. Oto przykłady:

  const  
    Liczba_ksiazek : word = 0; { na początku }
      { katalog jest pusty }
    Liczniki : array[1..4] of word = (0, 0, 0, 0);
      { inicjalizacja tablicy }

Wiadomości przedstawione w tym rozdziale powinny pozwolić nam na napisanie podstawowej wersji naszego programu. Tym jednak zajmiemy się już w następnym rozdziale.

Zapamiętaj

Poprzedni | Spis treści | Następny | Wersja spakowana |