Jeżeli nie zamierzasz zmieniać długości wierszy, a za to chciałbyś, aby przydzielony
tablicy obszar pamięci był ciągły, wystarczy odrobina arytmetyki na wskaźnikach:
&$7&+&D &
$7&+)+&D &
& &G &@@
$7&$7@&)+
7 Mówiąc ściśle, nie są to tablice, ale raczej obiekty używane jak tablice, zobacz pytanie 6.14.
138
Programowanie w języku C. FAQ
W obu przypadkach (to znaczy tablic , i ) tablice dynamiczne można in-
deksować za pomocą normalnych operatorów: Z/ (dla .01#*%+) i .0
/1#$%&'(#)). Poniższy rysunek przedstawia schematycznie „układ” wierszy w tabli-
cach , i .
Jeżeli dwa odwołania do pamięci w tym schemacie są z jakichś powodów nie do przy-
jęcia8, możesz zasymulować tablicę dwuwymiarową dynamicznie stworzoną tablicą
jednowymiarową:
&$6&+)+&D &
Jednak w takim przypadku obliczanie indeksów musisz wykonywać własnoręcznie.
Aby odwołać się do elementu o współrzędnych ,/, należałoby użyć wyrażenia -
2 /9. Takiej tablicy nie można jednak przekazać do funkcji, któ-
ra akceptuje tablicę wielowymiarową. Zobacz też pytanie 6.19.
Możesz też użyć wskaźników do tablic:
&$8.20345.1
&.20345.1+&D $8
8 Zauważ jednak, że dwukrotne odwołanie do tablicy nie musi być wcale mniej efektywne niż jawne mnożenie indeksów.
9 Jawne obliczanie indeksu można ukryć w makrodefinicji, na przykład: F & J$ %&%*
&)+@*. Jednak wywołanie takiej makrodefinicji, z nawiasami i przecinkami, nie kojarzyłoby się z normalną składnią dostępu do tablic. Makrodefinicja musiałaby mieć również dostęp do jednego z wymiarów tablicy.
Rozdział 6. ♦ Tablice i wskaźniki
139
albo nawet:
&$-./0 1.20345.1
&./0 1.20345.1+&D $-
Jednak składnia odwołania do elementów wskazywanych tablic robi się coraz bar-
dziej skomplikowana (w przypadku " trzeba pisać "/). Najwyżej
jeden wymiar tablicy można określić w czasie działania programu.
Używając tych technik, nie można oczywiście zapomnieć o zwolnieniu przydzielonej
pamięci, kiedy nie jest już potrzebna. W przypadku tablic , i wymaga to
kilku kroków (zobacz też pytanie 7.23):
& &G &@@
K&$&
K&$
K&$7
K&$7
Nie możesz też mieszać tablic przydzielonych dynamicznie ze zwykłymi, utworzo-
nymi statycznie (zobacz pytanie 6.20, a także 6.18).
Powyższe techniki można oczywiście rozszerzyć na trzy i więcej wymiarów. Oto „trój-
wymiarowa” wersja pierwszego sposobu:
&6&+&+&D &
& &G&+ &@@
6&&+$&+&D &
* *G$&+ *@@
6&*&+D&+&D &
Zobacz też pytanie 20.2.
6.17
Pytanie: Wpadłem na fajny pomysł — jeżeli napiszę:
& $
&$A $L
mogę traktować tablicę tak, jakby jej indeksy zaczynały się od 1. Czy to
poprawne?
Odpowiedź: Chociaż taka technika może wydawać się atrakcyjna (była nawet używa-
na w starszych wydaniach książki Numerical Recipes in C), nie jest zgodna ze Stan-
dardem języka C. Arytmetyka wskaźników zdefiniowana jest tylko, jeżeli wartość
wskaźnika pozostaje w obrębie tego samego przydzielonego bloku pamięci albo
umownego „końcowego” elementu tuż za nim. W przeciwnym wypadku zachowanie
programu jest niezdefiniowane, nawet jeżeli nie następuje dereferencja wskaźnika.
Kod w pytaniu oblicza wskaźnik do obszaru przed początkiem tablicy .
140
Programowanie w języku C. FAQ
W momencie odejmowania offsetu może wystąpić błąd wygenerowania nieprawidło-
wego adresu (na przykład gdyby obliczenie adresu spowodowało „zawinięcie” wokół
początku segmentu pamięci).
Referencje: K&R2 §5.3, §5.4, §A7.7
ANSI §3.3.6
ISO §6.3.6
Rationale §3.2.2.3
Funkcje a tablice wielowymiarowe
Trudno jest przekazywać tablice wielowymiarowe do funkcji z zachowaniem pełnej
ogólności. Podmiana parametrów tablicowych na wskaźniki (zobacz pytanie 6.4)
oznacza, że funkcja, która akceptuje zwykłe tablice, może również przyjmować tabli-
ce o dowolnej długości, co jest wygodne. Jednak podmiana dotyczy tylko najbardziej
„zewnętrznej” tablicy, pozostałe wymiary nie mogą być zmienne. Problem ten wyni-
ka z faktu, że w języku C wymiary tablic muszą być zawsze znane w czasie kompila-
cji. Nie można ich określić, przekazując na przykład dodatkowy parametr.
6.18