Ten idiom naturalnie wypada z alokacji tablicy 1D. Zacznijmy od przydzielenia tablicy 1D dowolnego typu T:
T *p = malloc( sizeof *p * N );
Proste, prawda? Wyrażenie *p ma typ T, więc sizeof *pdaje taki sam wynik jak sizeof (T), więc mamy wystarczająco dużo miejsca alokacji dla N-elementowe tablicy T. Dotyczy to każdego typuT .
Teraz Tzastąpmy typem tablicowym, takim jak R [10]. Wtedy staje się nasz przydział
R (*p)[10] = malloc( sizeof *p * N);
Semantyka tutaj jest dokładnie taka sama, jak w przypadku metody alokacji 1D; zmienił się tylko typ p. Zamiast T *tego jest teraz R (*)[10]. Wyrażenie *pma typ, Tktóry jest typem R [10], więc sizeof *pjest równoważne temu, sizeof (T)które jest równoważne sizeof (R [10]). Więc przydzielamy wystarczającą ilość miejsca na tablicę Nby 10elementu R.
Jeśli chcemy, możemy pójść jeszcze dalej; przypuśćmy, że Rjest typem tablicowym int [5]. Zastąp to Ri otrzymamy
int (*p)[10][5] = malloc( sizeof *p * N);
Ta sama umowa - sizeof *pjest taka sama jak sizeof (int [10][5])i kończymy przydzielanie ciągłego kawałka pamięci wystarczająco dużej, aby pomieścić tablicę Nby 10by . 5int
Więc to jest strona alokacji; a co ze stroną dostępową?
Pamiętaj, że []operacja z indeksem dolnym jest zdefiniowana w kategoriach arytmetyki wskaźnika: a[i]jest zdefiniowana jako *(a + i)1 . W ten [] sposób operator indeksu niejawnie odwołuje się do wskaźnika. Jeśli pjest wskaźnikiem do T, możesz uzyskać dostęp do wskazanej wartości albo przez jawne wyłuskiwanie za pomocą *operatora jednoargumentowego :
T x = *p;
lub korzystając z []operatora indeksu:
T x = p[0]; // identical to *p
Tak więc, jeśli pwskazuje na pierwszy element tablicy , możesz uzyskać dostęp do dowolnego elementu tej tablicy, używając indeksu na wskaźniku p:
T arr[N];
T *p = arr; // expression arr "decays" from type T [N] to T *
...
T x = p[i]; // access the i'th element of arr through pointer p
Teraz powtórzmy naszą operację podstawienia i zastąpmy Ttypem tablicy R [10]:
R arr[N][10];
R (*p)[10] = arr; // expression arr "decays" from type R [N][10] to R (*)[10]
...
R x = (*p)[i];
Jedna natychmiast widoczna różnica; jawnie usuwamy odwołanie pprzed zastosowaniem operatora indeksu dolnego. Nie chcemy indeksować do p, chcemy indeksować do tego, co p wskazuje na (w tym przypadku tablicę arr[0] ). Ponieważ jednoargumentowy *ma niższy priorytet niż []operator indeksu dolnego , musimy użyć nawiasów, aby jawnie grupować pz *. Ale pamiętajcie z góry, że *pto to samo co p[0], więc możemy to zastąpić
R x = (p[0])[i];
Lub tylko
R x = p[0][i];
Tak więc, jeśli pwskazuje tablicę 2D, możemy indeksować do tej tablicy w następujący psposób:
R x = p[i][j]; // access the i'th element of arr through pointer p;
// each arr[i] is a 10-element array of R
Biorąc to do tych samych wniosków, jak powyżej, zastępując Rw int [5]:
int arr[N][10][5];
int (*p)[10][5]; // expression arr "decays" from type int [N][5][10] to int (*)[10][5]
...
int x = p[i][j][k];
Działa to tak samo, jeśli pwskazuje na zwykłą tablicę lub jeśli wskazuje na przydzieloną pamięć malloc.
Ten idiom ma następujące zalety:
- To proste - tylko jeden wiersz kodu, w przeciwieństwie do fragmentarycznej metody alokacji
T **arr = malloc( sizeof *arr * N );
if ( arr )
{
for ( size_t i = 0; i < N; i++ )
{
arr[i] = malloc( sizeof *arr[i] * M );
}
}
- Wszystkie wiersze przydzielonej tablicy są * ciągłe *, co nie ma miejsca w przypadku powyższej metody alokacji fragmentarycznej;
- Zwolnienie tablicy jest równie łatwe za pomocą pojedynczego wywołania funkcji
free. Ponownie, nie jest to prawdą w przypadku fragmentarycznej metody alokacji, w której trzeba cofnąć przydział każdego z nich, arr[i]zanim będzie można zwolnić arr.
Czasami preferowana jest metoda alokacji fragmentarycznej, na przykład gdy sterta jest mocno pofragmentowana i nie można przydzielić pamięci jako ciągłego fragmentu lub chcesz przydzielić tablicę „postrzępioną”, w której każdy wiersz może mieć inną długość. Ale ogólnie jest to lepsza droga.
1. Pamiętaj, że tablice nie są wskaźnikami - zamiast tego wyrażenia tablicowe są w razie potrzeby konwertowane na wyrażenia wskaźnikowe.