C/Wskaźniki - więcej: Różnice pomiędzy wersjami

Usunięta treść Dodana treść
Bartem (dyskusja | edycje)
Podana była tylko symulacja tablicy dwuwymiarowej + drobne poprawki.
Lethern (dyskusja | edycje)
→‎Tablice wielowymiarowe: poprawki: 1) to nie C++, 2) złe zwalnianie pamięci, 3) uzupełnienie treści i tłumaczenia 4) wiele innych
Linia 218:
[[Grafika:Array of array storage.svg|thumb|180px|tablica dwuwymiarowa — w rzeczywistości tablica ze wskaźnikami do tablic]]
 
W rozdziale [[../Tablice/]] pokazaliśmy, jak tworzyć tablice wielowymiarowe, gdy ich rozmiar jest znany w czasie kompilacji. Teraz zaprezentujemy, jak to wykonać za pomocą wskaźników i to w sytuacji, gdy rozmiar może siębyć zmieniaćdowolny. Załóżmy, że chcemy stworzyć tabliczkę mnożenia - utworzymy do tego celu tablicę dwuwymiarową, w której oba wymiary będą miały ten sam rozmiar, pobrany od użytkownika:
<source lang="C">
int ROZMIARrozmiar, i;
printf("Podaj rozmiar tabliczki mnozenia: ");
scanf("%d", ROZMIAR&rozmiar);
int **tabliczka = (int **)malloc(ROZMIARrozmiar * sizeof (*tabliczka)); /* 1 */
for(int i=0; i<ROZMIARrozmiar; ++i) /* 2 */
{
*( tabliczka+[i)]= (int *)malloc(ROZMIARrozmiar * sizeof (**tabliczka));
}
 
/* tutaj można pętlą/funkcjami operującymi na pamięci zainicjować tabliczkę mnożenia */
/* i operować na niej. Gdy już wykonamy wszystkie czynności.. */
for(i=0; i<rozmiar; ++i) /* 3 */
free(*tabliczka);
free(tabliczka[i]);
free(tabliczka); /* 4 */
tabliczka = NULL;
</source>
 
Przydzielanie pamięci wygląda następująco: najpierw dla pierwszego wymiaru tablicy, który jest zarazem "tablicą tablic" (1) - tak jak na rysunku, jest to tablica przechowywująca wskaźniki do szeregu kolejnych tablic, w których przechowywane są już liczby. W drugim kroku (2) przydzielamy pamięć wszystkim tym tablicom. Tablica jest typu int* (wskaźnik na pierwszy element tablicy), natomiast tablica tablic jest typu int** (wskaźnik na pierwszy element, którym jest wskaźnik na tablicę). Jako że malloc jako argument przyjmuje rozmiar pamięci w bajtach, posługujemy się konstrukcją sizeof element. Pobiera ona typ i zwraca jego rozmiar, w naszym przypadku - (*tabliczka) oznacza to samo, co tabliczka[0], więc jest pierwszą podtablicą i ma typ int*. Jest to typ wskaźnikowy, dlatego sizeof zwróci ilość bajtów jakie wymaga wskaźnik. Drugim razem używamy jako argumentu (**tabliczka), co odpowiada użyciu tabliczka[0][0] (pierwsza liczba w pierwszej tablicy), które to ma już typ int - sizeof zwróci ilość bajtów odpowiadających zmiennej liczbowej. Dla systemu x86 dla obu sizeof powinniśmy otrzymać wielkość 4 bajty, jednak może to się zmienić dla różnych kompilatorów, a tym bardziej dla systemu x64.<br />
Najpierw musimy przydzielić pamięć - najpierw dla "tablicy tablic" (1) a potem dla wszystkich elementów tablicę (2). Ponieważ tablica jest typu int* to nasza tablica tablic będzie wskaźnikiem na int* czyli int**. Tablicę zwalniamy najpierw zwalniając wskaźnik na elementy tablic, a potem całą tablicę:
Tablicę zwalniamy najpierw zwalniając wszystkie podtablice (3), a potem tablicę główną (4). Należy nie pomylić kolejności - gdybyśmy najpierw zwolnili pamięć tablicy trzymającej wskaźniki na tablice, wówczas próba uzyskania dalszych tablic mogłaby się zakończyłaby błędem aplikacji (odczyt ze zwolnionej pamięci).
 
<source lang="C">
free(*tabliczka);
free(tabliczka);
</source>
 
Należy nie pomylić kolejności. Gdybyśmy najpierw pozbyli się wskaźnika na wskaźniki, a dopiero potem pozbyli się poszczególnych wskaźników na tablice, program będzie próbował odwołać się do pamięci, która nie była mu przydzielona.
 
Możemy również symulować tablicę dwuwymiarową za pomocą tablicy jednowymiarowej:
<source lang="c">
int *tabliczka = (int *)malloc(ROZMIARrozmiar * ROZMIARrozmiar * sizeof (*tabliczka));
</source>
W tym przypadku alokujemy pamięć równą liczbie elementów tablicy dwuwymiarowej, jednak w tablicy jednowymiarowej. Na przykład, dla rozmiaru równego 5 zaalokujemy tablicę jednowymiarową z 25 elementami. W ten sposób wszystkie elementy tablicy znajdą się w pamięci obok siebie, jednak utrudnia to programiście dostęp do nich, a także operacje na nich (potrzebna jest do tego [[#Arytmetyka wskaźników|Arytmetyka wskaźników]]).
 
Zauważmy, że wnie tenjesteśmy sposóbograniczeni możemywyłącznie uzyskaćdo nie tylko normalną,tablic "kwadratowąkwadratowych" tablicę (dla dwóch wymiarów). Możliwe jest też np. uzyskanie tablicy dwuwymiarowej trójkątnej:
0123
012
Linia 260 ⟶ 257:
<source lang="C">
const size_t wymiary[] = { 2, 4, 6, 8, 1, 3, 5, 7, 9 };
const size_t ilosc_podtablic = sizeof (wymiary) / sizeof (*wymiary);
int i;
int **tablica = malloc((sizeof wymiary / sizeof *wymiary)ilosc_podtablic * sizeof (*tablica));
for (i = 0; i<10ilosc_podtablic; ++i) {
tablica[i] = malloc(wymiary[i] * sizeof **tablica);
}
</source>
 
Gdy nabierzesz wprawy w używaniu wskaźników oraz innych funkcji malloc i realloc, nauczysz się wykonywać różne inne operacje, takie jak dodawanie kolejnych wierszy, usuwanie wierszy, zmiana rozmiaru wierszy, zamiana wierszy miejscami itp.
 
== Wskaźniki na funkcje ==