Tablice dynamiczne w Pascalu

Patkoss

Tablice dynamiczne w języku Pascal.

Czytelnicy zaznajomieni z Borland Delphi z pewnością wiedzą, że rozszerzona składnia języka Object Pascal pozwala na definiowanie tablic otwartych, o zmieniającej się liczbie elementów w trakcie wykonywania programu, innymi słowy dynamicznych. Niestety język Pascal został rozwinięty dla systemu Windows (i nadal jest rozwijany przez Borland'a), a co z tymi, którzy piszą programy dla trybu znakowego. Użytkownicy Delphi nie mogą narzekać, środowisko to pozwala tworzyć programy konsolowe, a co z programistami Pascala w systemach Linux (Unixowych). Tam niestety rzuca się w oczy trochę dyskomfort w pracy z narzędziami GNU Pascal, konfigurując jednak odpowiednio środowisko pracy (np. edytor Emacs) można osiągnąć nie mniejszą wygodę. Również i tam sytuację ratuje firma Borland ze swoim znakomitym Kylix'em, w ten sposób rozwijając język Pascal i dla systemów Unixowych. Ale co z tablicami dynamicznymi? Możemy się tutaj (nie korzystając z języka Object Pascal) posłużyć sposobem znanym z języka C. Wykorzystamy, oczywiście wskaźniki. Po pierwsze zadeklarujmy sobie nowy typ danych i nazwijmy go macierz:

type matrix=^byte;

Jak widzimy jest to typ wskaźnikowy, stwórzmy zmienną tego typu :

var m: matrix;

i w ciele programu przydzielmy jej w pamięci np. 10 bajtów:

getmem (m, 10);

teraz powróćmy do klauzuli deklaracji zmiennych i dodajmy dwie zmienne, które zapamiętają położenie w pamięci początku naszej tablicy:

var s, o: word;

Jedna zmienna zapamięta segment, a druga przesunięcie względem segmentu:

s:=seg(m);
o:=ofs(m);

Teraz możemy już zwolnić nasz wskaźnik, powodując, że pamięć nie będzie już dla niego rezerwowana:

freemem (m, 10);

Pamięć uprzednio zajmowana jest zwolniona, my mamy namiary do niej, wieć nic nie stoi na przeszkodzie aby wykorzystać tablicę (przy wykorzystaniu tablicy predefiniowanej mem):

mem[s:o]:=25;
mem[s:o+1]:=50;

Tym sposobem możemy pamiętać sobie zmienne w pamięci, nie zajmując miejsca w stosie zmiennych. Oczywiście odczyt przebiega w sposób analogiczny, np.:

writeln (mem[s:o]);
writeln (mem[s:o+1]);

Na ekranie powinniśmy zobaczyć nasze dwie liczby. Rozwiązanie takie ma ogromną wadę. Jeżeli chcielibyśmy coś jeszcze umieścić w pamięci należy dbać o to aby nie dochodziło do kolizji, inaczej będziemy zamazywać sobie dane. Jeżeli zechcemy umieścić jeszcze jedną zmienną dynamiczną, to po zwolnieniu poprzedniej jest ogromne prawdopodobieństwo, że zamażemy sobie tablice. Ale w sytuacji, gdy nie korzystamy ze zmiennych dynamicznych, pisząc proste programy można wykorzystać dostępną pamięć w ten sposób.

6 komentarzy

Może to nie jest tak dynamicznie jak autor by chciał, ale dobrym rozwiązaniem są choćby kolekcje (TCollection). Dos?ęp może nie taki jak w Delphi czy w językach skryptowych (np. w PHP tablica jest po niekąd odpowiednikiem koleckji; TCL - tu też podobnie i dostęp też podobny), ale sporo podobieństw (przynajmniej ja zauważam :D) Minusem tej metody jest ograniczenie do 16384 (jak nie 16383 :D) elementów, ale... dla chcącego nic trudnego.

Innym sposobem są listy jedno- dwu- kierunkowe (są też i inne, ale nie wnikałem w ich szczegóły aż tak bardzo jak w te :P). Tu już nie ma limitu 16K elementów... a Protect-Mode sprawia, że jest to wyśmienita sprawa dla dużej ilości danych. Jak wszystko, listy mają swoją ogromną wadę. "Czas dostępu" do danych jest wydłużony o przeszukiwanie listy od początku do konkretnego elementu.

Wiadome... wszystko ma swoje zastosowanie, tylko trzeba wiedzieć gdzie i kiedy je wykożystać.

A co do dynamiczności w artykule, to nie wiem, czy bym skusił się na przydzielanie 10 bajtów dla zmiennej typu ^Byte. Bardziej eleganckim sposobem jest:
Type
PByteArray = ^TByteArray;
TByteArray = Array[0..65535] of byte;

Var
tab:PByteArray;

Begin
GetMem(tab,1000);
...
FreeMem(Tab,1000);
End.

Wszystko sprawa gustu i upodobań :D
Pozdrawiam.

Może free pascal ma już lepsze mechanizmy :]
Jest nowszy :) Tak czy inaczej C++ ma lepszą obsługę tablic dynamicznych :) Kurcze, czemu w szkole uparła się na Pascal'a....

A u mnie na FPC wyskakuje błąd:
"Error: identifier not found 'mem' ",
a następny błąd:
"Fatal: Syntax error, ']' expected, but ':' found"

Jeśli system jest wielozadaniowy (i nie jest to Win9x), to zapewnia ochronę pamięci i programy nie mogą pisać nawzajem po cudzej pamięci.

W systemach wielozadaniowych może być niestety problem z zabieraniem sobie nawzajem pamięci przez programy.
Wydaje mi się, że chyba łatwiej już wykorzystać listy i inne twory dynamiczne.

W FPC trzeba zadeklarować użycie jakiegoś dodatkowego modułu żeby używać "mem". To logiczne, bo kto to widział, żeby w XXI wieku takie dziwy uprawiać, jak ręczne pisanie po niezalokowanej pamięci. Herezja po prostu.

Poza tym FPC ma tablice dynamiczne, których długość można zmieniać za pomocą SetLength(Tab), więc nie trzeba nawet samemu dbać o alokację i zwalnianie pamięci. A dla tych co lubią (dla mnie):

Var
Tab : Pointer;

Begin
GetMem(Tab, 1024);
Byte((Tab+10)^):=100;
DWord((Tab+11)^):=$10000000;
WriteLn(Byte((Tab+10)^)); // 100
WriteLn(DWord((Tab+11)^)); // 268435456
FreeMem(Tab, 1024);
End.