background image

Struktury i typy danych 

 

Do czego służą 

 

W Turbo Pascalu można używać różnych formatów danych, takich jak liczba, napis, znak. 
Istnieją różne typy, niektóre zajmują mniej pamięci i są mniej dokładne, inne zajmują więcej 
pamięci, za co mają większą precyzję. Te dłuższe zazwyczaj wymagają też więcej czasu 
pracy procesora. Podstawowe typy danych zostały opisane w rozdziale Pierwsze programy, 
Zmienne. 

 

Struktury danych pozwalają komputerowi w łatwiejszy sposób przetwarzać informacje, 
szybciej zapisywać do pliku, lepiej organizować w pamięci, szybciej wyszukiwać i ułatwić 
pracę programiście. 

 

Tablice 

 

W bibliotece znajdują się szafki z kartotekami skatalogowanych książek. W każdej szafce są 
takie kartoteki. Tych szafek jest dość dużo. Są poustawiane obok siebie. Każda szuflada ma 
swój numer, czy literę. Są w nich tylko kartoteki. Jeśli biblioteka jest duża, regałów z 
kartotekami może być wiele. 

 

Tablice są podobne do szuflad bibliotecznych. Tablica składa się z wielu 'schowków', 
poustawianych obok siebie w pamięci. W tablicy znajdują się tylko zmienne i to zmienne 
jednego typu. Każdy element tablicy ma swój indeks. Tablice mogą być wielowymiarowe, nie 
tylko w poziomie, ale i w pionie a nawet wgłąb. 

 

Do czego służą? Przechowują grupy danych. Mogą zawierać listę liczb. Mogą zawierać 
kartoteki pracowników -ich nazwiska, imiona, bądź całe rekordy z danymi. 

 

Aby je zadeklarować używa się w sekcji var wyrażenia: Array[{min}..{max}] of {...} 

 

background image

var tablica : Array[0..30] of Integer; 
 
   tablica_imion : Array[1..10] of string; 
 
   tab2 : Array[0..30, 1..3] of Integer; {tablica dwuwymiarowa} 
 

A jak się odwoływać w programie do tablic? 

 
Tablica[1] := 1; {zwykłe podstawienie} 
 
for i:= 0 to 30 do {w pętli...} 
  tablica[i] := 0; {wyzerowanie całej tablicy} 
 
tab2[i][1] := x; {dwuwymiarowa} 
tab2[i, 1] := x; {drugi sposób} 
 
{*****************************************} 
{wczytanie i wyświetlenie 10 imion w pętli} 
 
for i:= 1 to 10 do {w pętli...} 
begin 
  Write('Podaj ', i, ' imię: '); 
  ReadLn(tablica_imion[i]); 
end; 
 
WriteLn('Wpisane imiona:'); 
 
for i:= 1 to 10 do {w pętli...} 
begin 
  Write(tablica_imion[i]); 
end; 
 

Łatwo zauważyć, że do tablicy można się odwoływać za pomocą indeksu. I właściwie o to 
chodzi w używaniu tablic. Pozwalają znacznie zaoszczędzić czasochłonne wklepywanie kodu. 

 

Przypuśćmy, że w tablicy liczb Integer, o nazwie tablica chcesz znaleźć wartość największą. 
Jak to zrobić? Trzeba przejść przez całą tablicę i sprawdzić, czy kolejny element jest większy 
nich dotychczas największy. Uwaga, na początku elementem dotychczas największym musi 
być ten, który jest pierwszym elementem tablicy, nie 0. Dlaczego? Ponieważ gdy tablica 
będzie zawierała jedynie liczby ujemne, program uważałby, że największą dotychczasową 
wartością jest 0, a tak nie będzie. 

 
var tablica : Array[0..30] of Integer; 
   i, max : Integer; 

background image

 
begin 
{tutaj można wypełnić tablicę jakimiś wartościami, np. losowymi} 
Randomize; {Inicjowanie losowania} 
 
for i:= 0 to 30 do {w pętli...} 
  tablica[i] := Random(100) - Random(100); {losowanie w zakresie -99..+99} 
 
max := tablica[0]; 
for i:= 1 to 30 do {w pętli...} 
begin 
  {Jeśli największy dotychczas element jest mniejszy niż badany...} 
  if max < tablica[i] then 
    max := tablica[i];{zmień dotychczas największy} 
end; 
 
 {tu możesz wyświetlić max} 
WriteLn('Max: ', max); 
end. 
 

Własne typy danych 

 

Czasami zachodzi potrzeba utworzenia własnego typu -tzw. rekordu, który będzie 
przechowywał wymyślone przez nas dane. Przypuśćmy że chciałbyś zbudować prostą bazę 
danych zawierającą imiona i nazwiska swoich klientów. Zamiast przechowywać wszelkie 
dane w osobnych tablicach np. 

 
const 
 MaxDanych = 200; 
 
var 
   {imiona - tablica o rozmiarze MaxDanych elementów } 
   imiona     : array[0..MaxDanych-1] of string[20]; 
   nazwiska   : array[0..MaxDanych-1] of string[30]; 
   telefony   : array[0..MaxDanych-1] of string[20]; 
 
{...} 
{odwoływanie} 
 
imiona[10]:='Moje Imię'; 
nazwiska[10]:='Moje Nazwisko'; 
telefony[10]:='0700 00 000'; 
 

Można pogrupować je blisko siebie. W taki sposób by znajdowały się obok siebie w pamięci 
(jest bardziej fachowo). 

background image

 

Można to zrobić choćby tak: 

 
const 
   MaxDanych = 200; 
 
type 
   TDaneOsobowe = record 
     imie : string[20]; 
     nazwisko : string[30]; 
     telefon : string[20]; 
     rok_ur : integer; 
   end; 
 
var 
   Dane : array[0..MaxDanych-1] of TDaneOsobowe; 
 
{…} 
{odwoływanie} 
 
Dane[0].imie := 'Moje imie'; 
Dane[0].nazwisko := 'Moje nazwisko'; 
Dane[0].telefon := '0 700 00 000'; 
Dane[0].rok_ur := 1999; 
 
{lub za pomocą with – uproszczenie} 
with Dane[0] do 
begin 
 imie := 'Moje imie'; 
 rok_ur := 1999; 
end; 
 

W przykładzie zademonstrowano korzystanie z polecenia with - które dla każdego elementu 
wewnątrz dodaje przedrostek, w typ wypadku Dane[0]. Dzięki temu poleceniu możemy 
znacznie skrócić kod. 

 

Do tablicy własnego rekordu można odnosić się jak do każdej innej tablicy. Można 
przeglądać tą tablicę za pomocą pętli i zmiennej iterowanej (zwiększanej). 

 

Wskaźniki w turbo pascalu (pointer) 

 

background image

Wskaźniki przypominają spis treści w grubej książce. Gdybyś podczas rozmowy telefonicznej 
chciał wskazać koledze konkretne miejsce w jakiejś publikacji, co zrobisz? Czy powiesz mu, 
otwórz swoją książę na zdaniu... I tu zaczniesz cytować, czy raczej podasz numer strony? 
Oczywiście numery stron są lepsze, bo ułatwiają szybie odnajdywanie miejsc. Tak samo 
wskaźniki w programowaniu -pozwalają przeszukiwać spore ilości danych bez wertowania 
całej pamięci. Podobnie jak przy rozmowie przez telefon, tak samo przy porozumiewaniu się 
między procedurami w programie potrzeba jak najściślejszych, ale jak najszybszych 
informacji. 

 

Wskaźniki to adresy pamięci, które pokazują komputerowi miejsce gdzie znajdują się jakieś 
dane. Możesz przykładowo przechowywać w pamięci wskaźnik na obrazek bitmapowy, 
podczas gdy przechowywanie obrazu w tablicy byłoby niezbyt praktyczne. Pamięć do której 
odnoszą się wskaźniki można w każdej chwili zwalniać, deklarować i zmieniać. 

 

Bez wskaźników nie byłoby możliwe nieograniczone tworzenie nowych danych, np. w 
słowniku. Gdyby nie wskaźniki mielibyśmy z góry określoną liczbę słów, które słownik 
byłby w stanie zapamiętać. W grach komputerowych, liczba ludzików, zbudowanych 
elementów w strategii, zawsze byłaby z góry ograniczona. Nie moglibyśmy w systemie 
uruchomić większej liczby programów niż zaplanowano. 

 

Wskaźniki mogą wskazywać na dowolny obszar pamięci, a adres ten można zmieniać. Są 
pomocne podczas alokacji pamięci. Kiedy chcemy ją zadeklarować w czasie trwania 
programu, funkcja GetMem zwraca nam właśnie wskaźnik -czyli miejsce gdzie będziemy 
mogli wpisywać swoje dane. Gdybyśmy spróbowali wspisać swoje dane bez wcześniejszego 
zarezerwowania pamięci, w najlepszym wypadku zakończyłby się nasz program, w gorszym 
zawiesilibyśmy komputer. 

 
 
Budowa wskaźników* 

Ze względu na ryzoko zawieszenia programu wskaźniki są traktowane jako typy, które należy 
umiejętnie stosować. Wskaźniki na ogół zbudowane są z 4 bajtów. Pierwsze 2 określają 
segment danych, kolejne 2 ofset. 

 

Pamięć konwencjonalna (pierwszy 1MB pamięci komputera) podzielony jest na segmenty po 
16KB każdy. Tak więc 1 segment ma adres 0, drugi 1, trzeci 2, itd... Ofset to przemieszczenie 
względem segmentu. Dzięki ofsetowi możemy dojść do każdego jednego bajtu pamięci. 
Adres typu pointer składa się z dwóch bajtów segmentu, dwóch ofsetu i jest najczęściej 
zapisywany w kodzie szesnastkowym, dzięki czemu jest bardziej czytelny, np. 

background image

 
  

$a000:00, $b800:00 

 
 
Typ pointer 

Typ wskaźników nazywa się pointer. Gdy jakąś zmienną zadeklarujesz jako poiner, będzie 
ona wskaźnikiem. Wskaźniki mogą adresować różne typy zmiennych. Mogą adresować 
liczby, napisy, tablice, itd. Gdy adresują konkretny typ, nie deklaruje się ich jako pointer, 
tylko tworzy swój własny typ -o tym przeczytasz za moment. 

 
 
Podstawianie wskaźników 

Żeby wskaźnik wskazywał adres jakiejś zmiennej, możemy napisać: 

 
var 
  wskaznik : pointer; {lub podobny typ} 
 
{...} 
 
wskaznik := @nazwa_zmiennej; 
 

znacznik @ oznacza pobranie adresu zmiennej. Gdybyśmy chcieli teraz coś zapisać pod 
wskazanym miejscem wystarczy wywołać wskazywane miejsce, za pomocą znaku ^ 

 
 wskaznik^ := nowa_wartosc; 
 

W podanym przykładzie trzeba by wskaźnik był zadeklarowany jako wskaźnik na liczbę. 
Gdybyśmy bez rzutowania i bez określenia rodzaju wkaźnika próbowali mu podstawić 
wartość wystąpiłby błąd "illegal assigment". 

 

Jak zadeklarować wskaźnik na liczbę? 
np. 

 
type p_liczba = ^integer; {utworzenie nowego typu - wskaźnika na liczbę} 
var liczba : p_liczba; 
   i : integer; 
 

background image

begin 
   liczba := @i; 
   i:=2; 
   liczba^:=4; 
end. 
 
{teraz i = 4 a nie 2!} 
 

Można ewentualnie potraktować typ pointer jako wskaźnik na liczbę co nazywa się 
rzutowaniem -dzięki temu unikniemy błędu a  powiemy kompilatorowi, że wpisujemy w 
niego liczbę Integer 

 
 var liczba : pointer; 
     i : integer; 
 
begin 
   liczba := @i; 
   i:=2; 
   integer(liczba^):=4; 
end. 
 


Document Outline