Dziwaczny sposób przydzielania dwuwymiarowej tablicy?

W projekcie ktoś popchnął tę linię:

double (*e)[n+1] = malloc((n+1) * sizeof(*e));

Który rzekomo tworzy dwuwymiarową tablicę(n+1)*(n+1) podwaja.

podobno , mówię, bo jak na razie nikt, o co pytałem, nie mógł mi powiedzieć, co to dokładnie robi, ani skąd pochodzi, ani dlaczego ma działać (co niby ma, ale jeszcze nie kupuję).

Być może brakuje mi czegoś oczywistego, ale byłbym wdzięczny, gdyby ktoś mógł mi wyjaśnić Powyższy tekst. Bo osobiście Poczuj się lepiej, jeśli użyjemy czegoś, co naprawdę rozumiemy.

Author: Peter Mortensen, 2016-04-22

3 answers

Zmienna e jest wskaźnikiem do tablicy n + 1 elementów typu double.

Użycie operatora dereferencji na e daje typ bazowy e, który jest " array of n + 1 elements of type double".

Wywołanie malloc po prostu pobiera typ bazowy e (wyjaśnione powyżej) i pobiera jego rozmiar, mnoży go przez n + 1 i przekazuje ten rozmiar do funkcji malloc. Zasadniczo przydzielanie tablicy n + 1 tablic n + 1 elementów double.

 86
Author: Some programmer dude,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-22 12:51:55

Jest to typowy sposób dynamicznego przydzielania tablic 2D.

  • e jest wskaźnikiem tablicy do tablicy typu double [n+1].
  • sizeof(*e) dlatego podaje Typ typu pointed-at, który jest wielkością jednej tablicy double [n+1].
  • przydzielasz miejsce dla n+1 takich tablic.
  • ustawiasz wskaźnik tablicy e, aby wskazywał pierwszą tablicę w tej tablicy tablic.
  • to pozwala na użycie e jako e[i][j], aby uzyskać dostęp do poszczególnych elementów w 2D / align = "left" /

Osobiście uważam, że ten styl jest dużo łatwiejszy do odczytania:

double (*e)[n+1] = malloc( sizeof(double[n+1][n+1]) );
 56
Author: Lundin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-22 12:57:25

Ten idiom wypada naturalnie 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 *p daje taki sam wynik jak sizeof (T), więc przydzielamy wystarczająco dużo miejsca dla N-elementowej tablicy T. To prawda dla dowolnego typu T.

Teraz zastąp {[12] } typem tablicy jak R [10]. Wtedy nasz przydział staje się

R (*p)[10] = malloc( sizeof *p * N);

Semantyka jest tutaj dokładnie taka sama {[78] } jak metoda alokacji 1D; wszystko, co się zmieniło, to typ p. Zamiast T *, jest teraz R (*)[10]. Wyrażenie *p mA typ T, który jest typem R [10], więc {[15] } jest równoważne sizeof (T), które jest równoważne sizeof (R [10]). Więc przydzielamy wystarczająco dużo miejsca dla N przez 10 tablicę elementów R.

Możemy pójść jeszcze dalej, jeśli chcemy; Załóżmy, że {[33] } jest typem tablicy int [5]. Zastąp to dla R i otrzymamy

int (*p)[10][5] = malloc( sizeof *p * N);

Ta sama transakcja - sizeof *p jest taka sama jak sizeof (int [10][5]), i kończymy przydzielanie sąsiadującej części pamięci wystarczająco dużej, aby trzymać N przez 10 przez 5 tablicę int.

Więc to jest strona alokacji; a co z stroną dostępu?

Pamiętaj, że operacja [] indeks dolny jest zdefiniowana pod względem arytmetyki wskaźnika: a[i] jest zdefiniowana jako *(a + i)1. Tak więc operator indeksu dolnego [] implicite dereferuje wskaźnik. Jeśli p jest wskaźnikiem do T, możesz uzyskać dostęp do wartości pointed-to poprzez jawne dereferowanie za pomocą operatora uniary *:

T x = *p;

lub {[78] } używając operatora [] dolnego:

T x = p[0]; // identical to *p

Tak więc, jeśli p wskazuje na pierwszy element tablicy , możesz uzyskać dostęp do dowolnego elementu tablicy za pomocą indeksu dolnego 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, zróbmy naszą operację zastępczą ponownie i zastąp {[12] } typem 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 od razu widoczna różnica; jawnie dereferujemy p przed zastosowaniem operatora dolnego. Nie chcemy indeksować do p, chcemy indeksować do tego, co p wskazuje na (w tym przypadku tablica arr[0]). Ponieważ unary * ma niższy priorytet niż operator [] dolny, musimy użyć nawiasów do jawnej grupy p z *. Ale pamiętaj z powyżej *p jest to samo co p[0], więc możemy to zastąpić

R x = (p[0])[i];

Lub po prostu

R x = p[0][i];

Tak więc, jeśli p wskazuje na tablicę 2D, możemy indeksować ją przez p w następujący sposó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 tego samego wniosku jak powyżej i zastępując R 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];

To działatak samo jeśli p wskazuje na zwykłą tablicę, lub jeśli wskazuje na pamięć przydzieloną przez malloc.

Ten idiom ma następujące korzyści:

    To proste - tylko jedna linijka kodu, w przeciwieństwie do metody alokacji fragmentarycznej]}
  1. wszystkie wiersze przydzielonej tablicy są * sąsiadujące*, co nie ma miejsca w przypadku powyższej metody alokacji fragmentarycznej;
  2. Dealokacja tablicy jest tak samo prosta, jak pojedyncze wywołanie free. Ponownie, nie jest to prawdą w przypadku metody alokacji fragmentarycznej, gdzie musisz deallokować każdy arr[i], zanim będziesz mógł deallokować arr.

Czasami preferowana jest metoda alokacji fragmentarycznej, np. gdy sterta jest mocno rozdrobniona i nie można przydzielić pamięci jako przylegającej do siebie części lub chcesz przydzielić "poszarpaną" tablicę, w której każdy wiersz może mieć inną długość. Ale ogólnie rzecz biorąc, jest to lepszy sposób.


1. Pamiętaj, że tablice nie są wskaźnikami - zamiast tego wyrażenia array są konwertowane na wyrażenia wskaźnikowe jako konieczne.
 39
Author: John Bode,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-26 18:49:48