home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga MA Magazine 1997 #3
/
amigamamagazinepolishissue03-1
/
ma_1995
/
05
/
ami034.txt
< prev
next >
Wrap
Text File
|
1997-04-07
|
12KB
|
445 lines
/* wyraûenia zawarte w >...< proszë pogrubiê */
AMIGA E9
<a>Rafaî Wiosna
<txt>Hej, ludzie! Idzie wiosna, zarówno za oknem, jak i na
papierze. Tym razem przeprowadzë akcjë promocyjnâ kasety
"Greatest Hits" Budki Suflera. Jak miîo znów odegraê stare
ôwietne przeboje tej formacji. Czëôê z Czytelników moûe nie
wiedzieê, co to jest ta ta Budka Suflera. Przypominam wiëc, ûe
jest to POLSKI zespóî grajâcy DOBRÂ MUZYKË, który w dodatku
istnieje dîuûej niû niektórzy z Was. W nim zaczynali
Borysewicz, Urszula, Trojanowska... Lata osiemdziesiâte coraz
silniej powracajâ na scenë muzycznâ (u nas Perfect, Budka
Suflera, u nich np. Human Leauge). A i dobrze, bo duûa czëôê
"pamiëtliwych" kawaîków powstaîa wîaônie wtedy. Ale co ja plotë,
mamy rozwaûaê o Amiga E, a nie o muzyce...
<sr>Tablice
<txt>Bardzo czësto sië zdarza, ûe dane uûywane przez program
muszâ byê w pewien sposób poukîadane, gîównie dlatego, aby dostëp
do nich byî szybszy lub îatwiejszy. Najprostszym sposobem na
wprowadzenie takiego porzâdku jest tablica, w E oznaczana jako
typ ARRAY. Najpierw trochë teorii.
Skombinuj sobie kartkë papieru. Narysuj na niej prostokât.
Podziel go pionowymi liniami na mniejsze prostokâty. Proszë
bardzo! Masz tablicë! Jest ona jednowymiarowa, gdyû wszystkie
powstaîe prostokâty majâ wysokoôê tego, który narysowaliômy na
poczâtku. Poîoûenie danego prostokâta moûesz okreôliê tylko jednâ
liczbâ. Zacznij od pierwszego z lewej -- nad nim napisz zero.
Potem nad drugim od lewej napisz jedynkë i postëpuj tak z resztâ.
Teraz narysuj dowolnâ liczbë poziomych linii, od pierwszej do
ostatniej linii pionowej naszej tablicy. Widzisz? Uzyskaîeô
tablicë dwuwymiarowâ. Ponumeruj rzëdy tak poêwiartowanego
prostokâta-tablicy, poczynajâc od górnego (jemu nadaj numer 0).
Weú teraz kilka innych czystych kartek i przerysuj to, co jest na
pierwszej. Nastëpnie kaûdâ z kartek ponumeruj, zaczynajâc od
zera. Brawo! Zrobiîeô tablicë trójwymiarowâ (zaiste trudno w tym
przykîadzie pokazaê trzeci wymiar na jednej kartce, która jest
przecieû obiektem majâcym znikomâ wysokoôê -- a wiëc
dwuwymiarowym).
Teraz narysuj kropkë w dowolnym prostokâcie (tym maîym) na
dowolnej kartce. Spróbuj okreôliê jego poîoûenie, np. trzecia
kartka, czwarty rzâd, pierwsza kolumna. Moûna zapisaê to tak:
tablica[2,3,0]
Dlaczego nie [3,4,1]? A no dlatego, ûe przyjëliômy numeracjë od zera.
Ten maîy przykîad miaî wyjaôniê cechy typowej tablicy. A wiëc:
^* ma ona jeden lub wiëcej wymiarów,
^* ma skoïczonâ, i co waûniejsze, z góry okreôlonâ liczbë pozycji,
^* kaûdy element tablicy ma swój "adres".
Amiga E pozwala na definiowanie tablic, ale niestety tylko
>jednowymiarowych<. Robi sië to w nastëpujâcy sposób:
<l>DEF a[132]:ARRAY,
table[21]:ARRAY OF LONG,
ints[3]:ARRAY OF INT,
objs[54]:ARRAY OF mójobiekt
<txt>Jak widzisz, >wielkoôê< tablicy jest okreôlana w nawiasach
kwadratowych, natomiast >typ danych< jest okreôlany na samym
koïcu. Domyôlnym typem jest CHAR, czyli znak (bajt), ale moûe
to byê równieû LONG, INT i dowolny obiekt (o nich opowiem w
nastëpnych odcinkach). Zauwaû, ûe typ LONG moûe równie dobrze byê
wykorzystany jako wskaúnik do dowolnego innego obiektu.
Po zdefiniowaniu tablicy mamy swobodny dostëp do jej elementów.
Jak w wypadku naszego eksperymentu z kartkami i prostokâtami,
kaûdy z nich ma swój "adres". Poniewaû jednak to sîowo w
kontekôcie programowania uûywane jest do innego celu, umówmy sië,
ûe do okreôlenia poîoûenia elementu w tablicy bëdziemy uûywaê
angielskiego sîowa "indeks". (Tych, którzy chcâ mówiê "offset",
informujë, ûe jest to nawet i poprawne, ale przydatne tylko w
wypadku tablic jednowymiarowych o elementach typu CHAR...).
Pierwszy element w dowolnym wymiarze tablicy zawsze ma indeks 0,
drugi -- 1, trzeci -- 2 itp, aû do elementu ostatniego, n-tego (n
oznacza tu wartoôê, którâ okreôlamy przy definiowaniu tablicy, w
nawiasach kwadratowych), który ma indeks równy n-1. Moûe brzmi to
bardzo dziwnie i nieintuicyjnie, ale tak wîaônie dziaîajâ
komputery, a na domiar zîego Amiga E nie pozwala jeszcze na
okreôlenie podstawy obliczania indeksu tablicy, tak jak ma to
miejsce w inych jëzykach programowania, gdzie moûna zdefiniowaê
tablicë o wymiarze [1945:1999]. My musimy zadowoliê sië
sformuîowaniem [55].
Pora na przykîad. Spójrz na programik poniûej:
<l>DEF a[10]:ARRAY
PROC main()
DEF i
FOR i:=0 TO 9
a[i]:=i*i
ENDFOR
WriteF('Siódmy element tablicy ma wartoôê \d\n', a[6])
a[a[2]]:=10
WriteF('Teraz tablica wyglâda nastëpujâco:\n')
FOR i:=0 TO 9
WriteF(' a[\d] = \d\n', i, a[i])
ENDFOR
ENDPROC
<txt>Powinieneô zrozumieê caîy program bez problemu. Kîopoty
moûesz mieê jedynie z liniâ ósmâ. Spróbuj sië domyôliê, jak
zostanie ona wykonana. Tak jest, w tym wypadku indeks tablicy
jest pobierany z jednego z jej elementów! Na przyszîoôê pamiëtaj,
ûe stosowanie takich sztuczek moûe byê niebezpieczne -- nie tylko
zaciemnia kod úródîowy, ale teû moûe doprowadziê do zîego
dziaîania programu -- wystarczy, ûeby element tablicy, którego
wartoôê brana jest za offset, sam miaî wartoôê wiëkszâ od
rozmiaru tablicy... Nasz przykîadowy program powinien sprawdziê,
czy na pewno element a[2] jest mniejszy od 10, czyli
zadeklarowanego rozmiaru tablicy a[]. Jeûeli twój program zacznie
mazaê po nie istniejâcych elementach tablicy, dziwne rzeczy mogâ
sië zdarzyê. Zdarzenie moûe w ogóle nie zostaê zauwaûone, ale
takûe moûe to wywoîaê wizytë Guru, gdy nie istniejâcy indeks
wypadnie akurat na kawaîku wykonywanego lub majâcego sië wykonaê
kodu maszynowego.
Oto, co powyûszy program wypluwa na konsolë:
<l>Siódmy element tablicy ma wartoôê 36
Teraz tablica wyglâda nastëpujâco:
a[0] = 0
a[1] = 1
a[2] = 4
a[3] = 9
a[4] = 10
a[5] = 25
a[6] = 36
a[7] = 49
a[8] = 64
a[9] = 81
<txt>Pewnym uîatwieniem dla programisty jest skrót, który moûna
zastosowaê przy korzystaniu z pierwszego (zerowego) elementu
tablicy. Zamiast pisaê a[0], moûna po prostu napisaê a[].
>Wskaúniki do tablicy<
Gdy zadeklarujesz tablicë, jej poczâtek w pamiëci bëdzie zapisany w
zmiennej o nazwie takiej, jak tablica, ale bez nawiasów kwadratowych.
**************** RYSUNEK DO WYKONANIA PRZEZ GRAFICZNY ***************
Program: DEF a[20]:ARRAY OF INT
+--------+
|zmienna | +-------+
| 'a' | |indeks |
|--------| |+-----+|
| adres +----\ || typ ||
+--------+ \ +=======+
\
\
+-------+ +--\----+ +-------+ +-------+ +-------+
| ??? | | a[0] | | a[1] | | a[19] | | ??? |
Pamiëê: |+-----+| |+-----+| |+-----+| ... |+-----+| |+-----+|
|| ??? || || INT || || INT || || INT || || ??? ||
+=======+ +=======+ +=======+ +=======+ +=======+
*********************************************************************
Jak widaê na ilustracji, zmienna "a" jest wskaúnikiem do
zarezerwowanego obszaru pamiëci, który zawiera elementy tablicy.
Zarówno poniûej, jak i powyûej tego obszaru znajdujâ sië komórki
pamiëci oznaczone symbolem "???", nie naleûâce do tablicy. Zmiana
ich zawartoôci, np. "dziëki" niepoprawnemu indeksowi, moûe
prowadziê do zawieszenia sië komputera.
Spójrz na poniûszy przykîad wykorzystujâcy wskaúnik do tablicy:
<l>DEF a[10]:ARRAY OF INT
PROC main()
DEF ptr:PTR TO INT,i
FOR i:=0 TO 9
a[i]:=i
ENDFOR
ptr:=a
ptr++
ptr[]:=22
FOR i:=0 TO 9
WriteF('a[\d] = \d\n', i, a[i])
ENDFOR
ENDPROC
<txt>A oto jego rezultat:
<l>a[0] = 0
a[1] = 22
a[2] = 2
a[3] = 3
a[4] = 4
a[5] = 5
a[6] = 6
a[7] = 7
a[8] = 8
a[9] = 9
<txt>Zauwaû, ûe ta dziwna, na razie nie znana Ci, technika,
zaprezentowana w poprzednim listingu, w rezultacie zmienia drugi
element tablicy. Zapis "ptr++" zwiëksza wkaúnik "ptr" tak, aby
wskazywaî on nastëpny element tablicy a[]. Waûne jest to, ûeby
zmiennâ "ptr" zadeklarowaê jako PTR TO INT, czyli WSKAÚNIK DO
INT. INT, poniewaû tablica skîada sië z elementów typu INT.
Zapis "ptr" jest w tym wypadku, po odpowiednim zdefiniowaniu
zmiennej, równowaûny zapisowi "ptr[]", czyli "ptr[0]". Piszâc
wiëc "ptr[1]" otrzymamy nastëpny element wskazywany przez zmiennâ
"ptr", w tym wypadku trzeci element tablicy a[]. Przykîady
uûycia:
<l> ptr:=a -> ptr wskazuje na a[0]
ptr++ -> ptr wskazuje na a[1]
ptr[3]:=22 -> zmiana elementu a[4]
ptr[-1]:=99 -> zmiana elementu a[0]
<txt>Jak widzisz, jest moûliwe takûe stosowanie ujemnych
indekstów dla zmiennych typu PTR TO COÔTAM. Pamiëtaj jednak, ûe
taki styl programowania moûe byê zabójczy zarówno dla
programisty, jak i dla programu...
A propos brzydkiego stylu programowania, to obie poniûsze
definicje sâ poprawne:
<l>DEF a[20]:ARRAY OF INT
DEF a:PTR TO INT
<txt>Jeûeli uûyjesz tej drugiej, musisz sam zadbaê o
zarezerwowanie pamiëci dla tablicy, a takûe o to, aby za kilka
tygodni poîapaê sië, o co chodzi w programie...
Przed chwilâ pokazaîem, jak w E zwiëkszaê zmiennâ-wskaúnik do
elementu tablicy. Zmniejszanie takiej zmiennej (czyli ustawienie
wskaúnika na poprzedni element tablicy) jest równie proste. Robi
sië to tak:
<l>ptr--
<txt>Tak naprawdë zapis "ptr++" i "ptr--" to teû wskaúniki.
Pierwszy oznacza element wskazywany przez ptr PRZED ZWIËKSZENIEM
tej zmiennej, a drugi -- na element wskazywany przez ptr PO
ZMNIEJSZENIU zmiennej. Zapis wiëc:
<l>addr:=p
p++
<txt>to to samo co
<l>addr:=p++
<txt>a zapis
<l>p--
addr:=p
<txt>moûna zastâpiê przez
<l>addr:=p--
<txt>Moûesz spytaê, dlaczego piszë o uûywaniu "++" i "--" przy
operacjach na wskaúnikach. Czyû nie moûna po prostu dodawaê i
odejmowaê od wskaúników. Otóû moûna, ale przenosisz wtedy na
siebie koniecznoôê dbania o wielkoôê elementu, na który wskazuje
zmienna. Maîe piwo, jeûeli jest to bajt (CHAR), dwa (INT) lub
cztery (LONG). Problem przychodzi, jeûeli zdefiniujemy tablicë
tak, ûe jej elementy bëdâ obiektami:
<l>MODULE 'intuition/screens'
PROC main()
DEF ekrany[10]:ARRAY OF screen,
ekran
ekran:=ekrany
...
<txt>Jak teraz chcesz przejôê do nastëpnego ekranu w tablicy?
Moûesz zrobiê to tak:
<l> ekran:=ekran + SIZEOF screen
<txt>Ale czyû nie proôciej napisaê po prostu "ekran++"?
Jeûeli chcesz zobaczyê, co to obiekt (w jëzyku C... struktura!) i
jak wyglâda definicja obiektu "screen", to poszukaj go w wydruku
produkowanym przez polecenie:
<l>E:bin/ShowModule E:Modules/intuition/screens.m
<txt>
>Przekazywanie tablic do procedur<
Zacznijmy od tego, ûe przekazywanie tablic do procedur, jako
parametr, nie jest dozwolone w Amiga E. Na szczëôcie, korzystajâc
ze wskaúników moûna bardzo îatwo to zasymulowaê. Spójrz:
<l>DEF a[10]:ARRAY OF INT
PROC main()
DEF i
wypelniaj(a, 10)
FOR i:=0 TO 9
WriteF('a[\d] = \d\n', i, a[i])
ENDFOR
ENDPROC
PROC wypelniaj(ptr:PTR TO INT, x)
DEF i
FOR i:=0 TO x-1
ptr[]:=i
ptr++
ENDFOR
ENDPROC
<txt>Moûna teû potraktowaê zmiennâ "prt" w sposób bardziej
przypominajâcy tablicë:
<l>PROC wypelniaj(ptr:PTR TO INT, x)
DEF i
FOR i:=0 TO x-1
ptr[i]:=i
ENDFOR
ENDPROC
<txt>Natomiast zaawansowani kursanci, którzy w dodatku pamiëtajâ
mój wykîad sprzed miesiâca, w którym prezentowaîem alternatywnâ
postaê pëtli FOR..ENDFOR, mogâ wklepaê coô takiego:
<l>PROC wypelniaj(ptr:PTR TO INT, x)
DEF i
FOR i:=0 TO x-1 DO ptr[]++:=i
ENDPROC
<txt>W takich "skrótach" miîujâ sië programiôci piszâcy w C. Aby
nie zaciemniaê kodu úródîowego programu, proponujë nie uûywaê
takich sposobików. Kto wie, kiedy przyjdzie Ci po pewnym czasie
domyôlaê sië, co napisaîeô w tym kawaîku...
Powyûszy sposób przekazywania tablic do procedur ma wadë. Za
kaûdym razem musisz zadbaê o to, aby w definicji procedury,
której parametrem jest wskaúnik do tablicy, typ danej przez niego
wskazywanej zgadzaî sië z typem elementu przekazywanej tablicy.
Musisz teû w jakiô sposób przekazaê procedurze rozmiar tablicy.
Powyûszy przykîad dobrze obrazuje rozwiâzania obu tych problemów.