Rozdziaª 11 Wska¹niki, tablice dynamiczne wielowymiarowe 11.1 Wst p Identycznie, jak w przypadku tablic statycznych, tablica dynamiczna mo»e by tablic jedno-, dwu-, trójitd. wymiarow. Tablica dynamiczna dwuwymiarowa (i wi cej-wymiarowa) to tak naprawd tablica wska¹ników do poszczególnych wymiarów. Jaka pªynie z tego korzy±? Podczas deklaracji tablicy mamy peªn kontrol nad wielko±ci poszczególnych wymiarów, statycznie nie da si osi gn takich efektów: Generowanie tablicy dwuwymiarowej dynamicznej odbywa podobnie jak jednowymiarowej. Zamiast tworzy wska¹nik do jednego wymiaru, tworzymy tablic wska¹ników wskazuj cych na wymiary. Stwórzmy dwuwymiarow, dynamiczn tablice (macierz) o rozmiarach 4x2. Zauwa»my, i» takiej tablicy u»ywamy identycznie, jak tablicy statycznej dwuwymiarowej (ró»nica polega na tworzeniu tablic) i konieczno±ci usuwania pami ci w przypadku tablicy dynamicznej po sko«czeniu pracy z ni : 3 #i n c l u d e <s t r i n g. h> 4 5 i n t main ( ) 6 { 7 i n t ** macierz = ( i n t **) malloc ( s i z e o f ( i n t *) * 4) ; 8 9 macierz [ 0 ] = ( i n t *) malloc ( s i z e o f ( i n t ) * 2) ; 10 macierz [ 1 ] = ( i n t *) malloc ( s i z e o f ( i n t ) * 2) ; 11 macierz [ 2 ] = ( i n t *) malloc ( s i z e o f ( i n t ) * 2) ; 12 macierz [ 3 ] = ( i n t *) malloc ( s i z e o f ( i n t ) * 2) ; 278
14 macierz [ 0 ] [ 1 ] = 1 2 ; 15 16 printf ( "%d\n", macierz [ 0 ] [ 1 ] ) ; 17 18 free ( macierz [ 0 ] ) ; 19 free ( macierz [ 1 ] ) ; 20 free ( macierz [ 2 ] ) ; 21 free ( macierz [ 3 ] ) ; 22 free ( macierz ) ; 23 24 return 0 ; 25 } Przeanalizujmy inny przykªad. Stwórzmy tablic dynamiczn dwuwymiarow o rozmiarze 3x3. Generowanie tablicy dwuwymiarowej dynamicznej bardzo wygodnie robi za pomoc p tli. Ka»dy obrót p tli przypisuje wska¹nik do nowego wymiaru. Aby nie powtarza kodu, zawrzyjmy przydzielanie i zwolnianie pami ci w p tli: 3 #i n c l u d e <s t r i n g. h> 4 5 i n t main ( ) 6 { 7 i n t i ; 8 i n t ** tablica = ( i n t **) malloc ( s i z e o f ( i n t *) * 3) ; 9 10 f o r ( i=0; i<3; i++) 11 tablica [ i ] = ( i n t *) malloc ( s i z e o f ( i n t ) * 3) ; 12 tablica [ 0 ] [ 1 ] = 1 2 ; 14 15 printf ( "%d\n", tablica [ 0 ] [ 1 ] ) ; 16 17 f o r ( i=0; i<3; i++) 18 free ( tablica [ i ] ) ; 19 20 free ( tablica ) ; 21 22 return 0 ; 23 } Graczne przedstawienie powy»szego kodu: 279
Denicja tablicy wska¹ników: poni»ej deniujemy tablic, która przechowuje 10 wska¹nikóww do typu int - tutaj tablica ma 10 elementów i ka»dym z jej elementów jest wska¹nik, to znaczy,»e w elemencie tab[0] jest wska¹nik (mo»emy go u»y np. do utworzenia tablicy, albo przypisa mu adres zmiennej): 2 3 i n t main ( ) 4 { 5 i n t * tab [ 1 0 ] ; 6 7 i n t a = 1024, i ; 8 9 /* wskaznik w elemencie t a b l i c y 10 z pod indeksu 0 wskazuje 11 na zmienna a */ 12 tab [ 0 ] = &a ; 14 printf ( "%d\n", * tab [ 0 ] ) ; 15 16 /* wskaznik w elemencie t a b l i c y z pod indeksu 17 1 wykorzystamy do stworzenia t a b l i c y */ 18 tab [ 1 ] = malloc ( s i z e o f ( i n t ) * 3) ; 19 20 /* wpiszemy w a r t o s c i */ 21 f o r ( i=0; i<3; i++) 22 tab [ 1 ] [ i ] = 100; 23 24 /* i wyswietlimy */ 25 f o r ( i=0; i<3; i++) 26 printf ( "%d\n", tab [ 1 ] [ i ] ) ; 27 28 /* usuniemy t a b l i c e, na ktora wskazywal 29 wskaznik z elementu z pod indeksu 1 30 n a s z e j t a b l i c y */ 31 free ( tab [ 1 ] ) ; 32 33 return 0 ; 34 } Przydzielenie pami ci dla dwuwymiarowej tablicy o rozmiarze 3 x 4. Tablice dwu- i wi cej wymiarowe mo»emy utworzy statycznie, lub za pomoc wska¹ników (analogicznie, jak robili±my to dla tablic jednowymiarowych). Aby zdeniowa, uworzy tablic dwuwymiarow o rozmiarze 3 x 4 (czyli dwuwymiarow ) liczb caªkowitych, potrzebujemy wska¹nika na wska¹nik na typ int - czyli int **wsk (tablica 2-wymiarowa - 2 gwiazdki przy wska¹niku, 3 wymiarowa - 3 gwiazdki przy wska¹niku, 4 wymiarowa - 4 gwiazdki przy wska¹niku, itd...). Prosz spojrze na poni»szy kod: 280
3 4 i n t main ( ) 5 { 6 7 i n t ** t = malloc ( s i z e o f ( i n t *) * 4) ; 8 9 t [ 0 ] = malloc ( s i z e o f ( i n t ) * 3) ; 10 t [ 1 ] = malloc ( s i z e o f ( i n t ) * 3) ; 11 t [ 2 ] = malloc ( s i z e o f ( i n t ) * 3) ; 12 t [ 3 ] = malloc ( s i z e o f ( i n t ) * 3) ; 14 t [ 0 ] [ 1 ] = 4 ; 15 printf ( "%d\n", t [ 0 ] [ 1 ] ) ; 16 17 free ( t [ 0 ] ) ; 18 free ( t [ 1 ] ) ; 19 free ( t [ 2 ] ) ; 20 free ( t [ 3 ] ) ; 21 22 free ( t ) ; 23 24 return 0 ; 25 } Oraz na poni»szy rysunek: Widzimy,»e najpierw musimy przydzieli pami dla gªównej tablicy, która przechowuje wska¹niki do pozostaªych tablic, wykonujemy to za pomoc instrukcji: int **t = malloc(sizeof(int*) * 4);, sprawdzaj c, jaki rozmiar ma wska¹nik, gdy» elementami naszej gªównej tablicy s wska¹niki, st d musimy przydzieli pami dla wska¹ników, nie dla intów. Nast pnie dla ka»dego elementu tablicy, którym jest wska¹nik (wska¹nik jest w tab[0], tab[1], tab[2], tab[3]) przydzielamy pami na podtablice o rozmiarze 3 elementów, korzystaj c z funkcji malloc. Podtablice przechowuj inty, a nie wska¹niki, wi c mo»emy u»y instrukcji malloc(sizeof(int) * 3); (przydzielaj c pami, sprawdzamy rozmiar inta). Dost p do elementów tak utworzonej tablicy uzyskujemy tak, jak do elementów dwuwymiarowej statycznej tablicy, tj. t[0][1]. 281
Oczywi±cie powy»szy kod mo»na upro±ci u»ywaj c p tli: 3 4 i n t main ( ) 5 { 6 i n t ** t = malloc ( s i z e o f ( i n t *) * 4) ; 7 i n t i ; 8 f o r ( i=0; i<4; i++) 9 t [ i ] = malloc ( s i z e o f ( i n t ) * 3) ; 10 11 t [ 0 ] [ 1 ] = 4 ; 12 printf ( "%d\n", t [ 0 ] [ 1 ] ) ; 14 f o r ( i=0; i<4; i++) 15 free ( t [ i ] ) ; 16 free ( t ) ; 17 18 return 0 ; 19 } Zwalniaj c tablic, usuwamy tylko miejsce zarezerwowane dla wska¹ników. Tak utworzon tablic mo»emy przekaza do funkcji: 1 void funkcja ( i n t ** tab, i n t n, i n t m ) 2 { 3 //... 4 } 5 i n t main ( ) 6 { 7 i n t ** t = malloc ( s i z e o f ( i n t *) * 4) ; 8 i n t i ; 9 f o r ( i=0; i<4; i++) 10 t [ i ] = malloc ( s i z e o f ( i n t ) * 3) ; 11 12 funkcja ( t, 4, 3) ; 14 f o r ( i=0; i<4; i++) 15 free ( t [ i ] ) ; 16 free ( t ) ; 17 18 return 0 ; 19 } lub te» zwróci z funkcji: 1 i n t ** funkcja ( i n t n, i n t m ) 2 { 3 i n t ** t = malloc ( s i z e o f ( i n t *) * n ) ; 4 i n t i ; 5 f o r ( i=0; i<n ; i++) 6 t [ i ] = malloc ( s i z e o f ( i n t ) * m ) ; 7 return t ; 8 } 9 i n t main ( ) 10 { 11 i n t n = 4, m = 3 ; 12 i n t ** t = funkcja ( n, m ) ; 14 f o r ( i=0; i<n ; i++) 15 free ( t [ i ] ) ; 16 free ( t ) ; 282
17 18 return 0 ; 19 } Analogicznie mo»emy przekaza do lub zwróci z funkcji tablic dynamiczn trójwymiarow, czterowymiarow, pi ciowymiarow, itd. Kod przedstawiony powy»ej mo»na równie» zamkn w 2 funkcjach: 1 i n t ** allocateit ( i n t n, i n t m ) 2 { 3 i n t i ; 4 i n t ** t = malloc ( s i z e o f ( i n t *) * n ) ; 5 f o r ( i=0; i<n ; i++) 6 t [ i ] = malloc ( s i z e o f ( i n t ) * m ) ; 7 return t ; 8 } 9 10 void freeit ( i n t **t, i n t n, i n t m ) 11 { 12 i n t i ; f o r ( i=0; i<n ; i++) 14 free ( t [ i ] ) ; 15 free ( t ) ; 16 } Dla 3-wymiarowej tablicy utworzonej za pomoc wska¹ników, rysunek wygl da nast puj co: Jak b dzie wygl da przydziaª pami ci dla takiej tablicy? 3 4 i n t main ( ) 5 { 6 7 i n t *** t = malloc ( s i z e o f ( i n t **) * 2) ; 8 9 t [ 0 ] = malloc ( s i z e o f ( i n t *) * 3) ; 10 t [ 1 ] = malloc ( s i z e o f ( i n t *) * 3) ; 11 12 t [ 0 ] [ 0 ] = malloc ( s i z e o f ( i n t ) * 4) ; t [ 0 ] [ 1 ] = malloc ( s i z e o f ( i n t ) * 4) ; 14 t [ 0 ] [ 2 ] = malloc ( s i z e o f ( i n t ) * 4) ; 15 283
16 t [ 1 ] [ 0 ] = malloc ( s i z e o f ( i n t ) * 4) ; 17 t [ 1 ] [ 1 ] = malloc ( s i z e o f ( i n t ) * 4) ; 18 t [ 1 ] [ 2 ] = malloc ( s i z e o f ( i n t ) * 4) ; 19 20 free ( t [ 0 ] [ 0 ] ) ; 21 free ( t [ 0 ] [ 1 ] ) ; 22 free ( t [ 0 ] [ 2 ] ) ; 23 24 free ( t [ 1 ] [ 0 ] ) ; 25 free ( t [ 1 ] [ 1 ] ) ; 26 free ( t [ 1 ] [ 2 ] ) ; 27 28 free ( t [ 0 ] ) ; 29 free ( t [ 1 ] ) ; 30 31 free ( t ) ; 32 33 return 0 ; 34 } Powy»szej tablicy mo»emy u»ywa tak, jak zwykªej statycznej, trójwymiarowej tablicy, to znaczy,»e dost p do pojedynczego elementu tablicy uzyskujemy za pomoc trzech indeksów, na przykªad: t[0][1][3] = 5; czy printf("%d", t[0][1][3]);. W jaki sposób za pomoc p tli upro±ci powy»szy kod i zamkn go w 2 funkcje - alokuj c i zwalniaj c pami do takiej tablicy? 1 double *** Allocate_3D_Double_Array ( i n t x, i n t y, i n t z ) 2 { 3 i n t i, j, k ; 4 5 double *** the_array = malloc ( s i z e o f ( double **) * x ) ; 6 7 f o r ( i = 0 ; i < x ; ++i ) 8 { 9 the_array [ i ] = malloc ( s i z e o f ( double *) * y ) ; 10 11 f o r ( j = 0 ; j < y ; ++j ) 12 { the_array [ i ] [ j ] = malloc ( s i z e o f ( double ) * z ) ; 14 15 f o r ( k = 0 ; k < z ; ++k ) 16 { 17 the_array [ i ] [ j ] [ k]= 0. 2 ; 18 } 19 } 20 } 21 return the_array ; 22 } 23 24 void release_3d_double_array ( double *** the_array, i n t x, i n t y, i n t z ) 25 { 26 i n t i, j ; 27 28 f o r ( i = 0 ; i < x ; ++i ) 29 { 30 f o r ( j = 0 ; j < y ; ++j ) 31 { 32 free ( the_array [ i ] [ j ] ) ; 33 } 34 free ( the_array [ i ] ) ; 35 } 36 free ( the_array ) ; 37 } 284
Przykªad: Rozwa»my ponizszy przykªad. funkcj main: Jest to kod, w którym wykorzystujemy 5 funkcji (w tym 1. funkcja int ** utworz(int n, int m); - funkcja ta pobiera 2 argumenty - wymiary 2 wymiarowej tablicy. Zadaniem funkcji jest zwrócenie dynamicznie utworzonej tablicy (tablicy utworzonej za pomoc wska¹ników). Funkcja przydziela okre±lony rozmiar pami ci (alokuje pami na tablic o wymiarach n na m, a nast pnie j zwraca (mo»emy róznie» powiedzie,»e zwraca wska¹nik do bloku pami ci przydzielonego na tablic ). 2. funkcja void wyswietl(int **tab, int n, int m); - funkcja ta pobiera 3 argumenty - wymiary 2 wymiarowej tablicy, oraz wska¹nik na wska¹nik do typu int (czyli inaczej -dwuwymiarow, dynamiczn tablic elementów typu int). Zadaniem funkcji jest wy±wietlenie wszystkich elementów tablicy. Poniewa» jest to tablica 2-wymiarowa, potrzebujemy do tego 2 p tli. 3. funkcja void usun(int **tab, int n, int m); - funkcja ta pobiera 3 argumenty - wymiary 2 wymiarowej tablicy, oraz wska¹nik na wska¹nik do typu int (czyli inaczej -dwuwymiarow, dynamiczn tablic elementów typu int). Zadaniem funkcji jest usuni cie dynamicznie przydzielonej pami ci na tablic (zwolnienie pami ci, usuni cie tablicy). 4. funkcja void wypelnij(int **tab, int n, int m, int x); - funkcja ta pobiera 4 argumenty - wymiary 2 wymiarowej tablicy, oraz wska¹nik na wska¹nik do typu int (czyli inaczej -dwuwymiarow, dynamiczn tablic elementów typu int) oraz warto±, któr ma wpisa do ka»dej komórki tablicy (warto±, któr nale»y wypeªni caª tablic ). Zadaniem funkcji jest wpisanie do ka»dej jej komórki warto±ci x. Dzi ki temu,»e wykorzystujemy funkcje, kod jest przejrzysty i czytelny. Mo»emy za ich pomoc tworzy nieograniczon liczb ró»nych tablic, wykonywa na nich dziaªania, i ostatecznie zwalnia przydzielon na nie pamie. W gªównej funkcji programu tworzymy 2 tablice - tab1, oraz tab2, o ró»nych wymiarach. Nast pnie wypeªniamy, wy±wietlamy tablice i usuwamy przydzielon pami. Zadanie to jest bardzo proste przy u»yciu funkcji. 3 4 i n t ** utworz ( i n t n, i n t m ) { 5 i n t ** tab, i ; 6 7 tab = malloc ( s i z e o f ( i n t *) * n ) ; 8 9 f o r ( i=0; i<n ; i++) 10 tab [ i ] = malloc ( s i z e o f ( i n t ) * m ) ; 11 12 return tab ; } 14 15 void wyswietl ( i n t ** tab, i n t n, i n t m ) { 16 i n t i, j ; 17 18 f o r ( i=0; i<n ; i++) 19 { 20 f o r ( j=0; j<m ; j++) 21 { 22 printf ( "%d ", tab [ i ] [ j ] ) ; 23 } 24 printf ( "\n" ) ; 25 } 26 } 285
27 28 void wypelnij ( i n t ** tab, i n t n, i n t m, i n t x ) 29 { 30 i n t i, j ; 31 32 f o r ( i=0; i<n ; i++) 33 { 34 f o r ( j=0; j<m ; j++) 35 { 36 tab [ i ] [ j ] = x ; 37 } 38 } 39 } 40 41 void usun ( i n t ** tab, i n t n, i n t m ) 42 { 43 i n t i ; 44 f o r ( i=0; i<n ; i++) 45 free ( tab [ i ] ) ; 46 free ( tab ) ; 47 } 48 49 i n t main ( i n t argc, char ** argv ) 50 { 51 i n t ** tab1, n1 = 2, m1 = 3 ; 52 53 tab1 = utworz ( n1, m1 ) ; 54 wypelnij ( tab1, n1, m1, 28) ; 55 wyswietl ( tab1, n1, m1 ) ; 56 usun ( tab1, n1, m1 ) ; 57 58 printf ( " \n" ) ; 59 60 i n t ** tab2, n2 = 5, m2 = 8 ; 61 62 tab2 = utworz ( n2, m2 ) ; 63 wypelnij ( tab2, n2, m2, 100) ; 64 wyswietl ( tab2, n2, m2 ) ; 65 usun ( tab2, n2, m2 ) ; 66 67 return 0 ; 68 } 286
11.2 Zadania do wykonania Zadanie 0 Napisz program, w którym stworzysz dynamiczn tablic 2-wymiarow o elementach typu int, i wymiarach wczytanych od u»ytkownika. Nast pnie, po utworzeniu tablicy wczytaj do niej liczby od u»ytkownika i wypisz caª tablic na ekranie w postaci macierzy (tzn. ka»dy wiersz tablicy 2-wymiarowej ma znale¹ si w nowym wierszu). Zwolnij pami zaalokowan na tablic. Zadanie 1 Napisz funkcj, która dostaje jako argument dodatnie liczby caªkowite n, m, tworzy dynamiczn dwuwymiarow tablic tablic o wymiarach n na m i zwraca jako warto± wska¹nik do niej. Zadanie 2 Napisz funkcj, która dostaje jako argumenty wska¹nik do dwuwymiarowej tablicy tablic oraz jej wymiary dodatnie liczby n, m i usuwa z pami ci otrzyman tablic. Zadanie 3 Napisz wersje zada«1 i 2 dla trzywymiarowych tablic tablic. Zadanie 4 Napisz funkcj, która dostaje w argumentach tablic dwuwymiarow o elementach typu int oraz jej wymiary n, m, i zwraca jako warto± sum warto±ci elementów tablicy. Zadanie 5 Napisz funkcj, która dostaje jako argumenty dwie tablice dwuwymiarowe o elementach typu int oraz ich wymiary i zamienia zawarto±ci obu tablic. Zadanie 6 Napisz funkcj przyjmuj c jako argumenty trzy wska¹niki na dynamicznie zaalokowane tablice dwuwymiarowe (tablice wska¹ników), reprezentuj ce macierze kwadratowe 10 x 10 o elementach zmiennoprzecinkowych. Funkcja powinna pomno»y dwie pierwsze macierze a wynik umie±ci w trzeciej. Zadanie 7 Napisz funkcj, która otrzymuje w argumentach dwuwymiarow tablic tablic (tablic dynamiczn ) oraz jej wymiary i zeruje elementy tablicy nale» ce do wierszy o nieparzystych indeksach (przyjmujemy,»e dwa elementy nale» do tego samego wiersza je»eli maj tak sam pierwsz wspóªrz dn ). Zadanie 8 Napisz funkcj, która przyjmie w argumencie dwuwymiarow tablic tablic liczb caªkowitych oraz jej dwa wymiary n i m, a nast pnie wy±wietli na ekran wiersze, których suma dodatnich elementów jest wi ksza od 100. Wy±wietlane liczby maj by oddzielane przecinkami, ka»dy wiersz w oddzielnej linii. Zadanie 9 Napisz funkcj, która otrzymuje jako argument dynamiczn dwuwymiarow tablic tab o wymiarach N x N (kwadratowa) przechowuj c elementy typu int oraz rozmiar N. Funkcja ma zwróci wska¹nik do dynamicznie zaalokowanej tablicy intów, o dªugo±ci równej iloczynowi elementów le» cych na przek tnych macierzy (tablicy) podanej jako argument. Zadanie 10 Napisz funkcj, która jako parametr przyjmie liczb caªkowit n. Funkcja ma zaalokowa dynamiczn dwuwymiarow trójk tn tablic liczb caªkowitych, wypeªni j zerami i zwróci jej adres. Na przykªad dla n = 5 tablica powinna wygl da nast puj co: 287
Liczba wierszy w trójk tnej tablicy równa jest n, natomiast liczba kolumn w ka»dym wierszu jest o 1 wi ksza od liczby kolumn w wierszu poprzednim. Wiersz ostatni ma liczb kolumn równ liczbie wierszy, czyli n. Zadanie 11 Napisz funkcj, która przyjmie dynamicznie alokowan tablic dwuwymiarow typu double i jej oba wymiary. Funkcja powinna zwróci numer wiersza, w którym suma kwadratów elementów jest najmniejsza. Zadanie 12 Napisz funkcj, która otrzymuje w argumentach dwuwymiarow dynamiczn tablic oraz jej wymiary n oraz m i sumuje wszystkie elementy tablicy nale» ce do wierszy o parzystych indeksach (przyjmujemy,»e dwa elementy nale» do tego samego wiersza je»eli maj tak sam pierwsz wspóªrz dn ), a nast pnie zwraca tak obliczon sum. Zadanie Napisz funkcj, która przyjmuje w argumencie dwuwymiarow tablic liczb zmiennoprzecinkowych oraz jej wymiary n i m. Funkcja ma wyzerowa kolumny, w których suma warto±ci elementów jest ujemna. Zadanie 14 Napisz funkcj, która dostaje jako parametr dynamiczn dwuwymiarow tablic liczb caªkowitych i jej wymiary n, m. Funkcja ma zwróci 1, je±li na brzegach (tzn. w pierwszym i ostatnim wierszu oraz w pierwszej i ostatniej kolumnie) tablicy wyst puj warto±ci tylko niezerowe; w przeciwnym razie funkcja ma zwróci 0. 288