Rozdziaª 9 Wska¹niki, tablice dynamiczne jednowymiarowe, staªe 9.1 Wst p Czym w C jest wska¹nik? Wska¹nik jest zmienn, która zawiera adres (wskazanie) innej zmiennej lub adres dowolnego obszaru w pami ci komputera, (np. mo»e by to adres obszaru danych lub adres kodu programu). Jak wiemy, poznane dot d zmienne (typów int, double, float, char, void) przechowywane s w pami ci RAM komputera dost pnej dla programu. Przykªadowo, gdy w programie zdeniujemy zmienn typu int i nadamy jej warot± : 5 i n t a = 104; 6 return 0 ; 7 } zmienna ta zostanie umieszczona w komórce pami ci RAM (dost pnej dla naszego programu) pod okre- ±lonym adresem: Dla powy»szego obrazka przykªadowy wska¹nik, który wskazywaªby na zmienn a, przechowywaªby jej adres w pami ci, czyli warto± 6579. 9. Deklarowanie wska¹ników Poniewa» wska¹nik jest zmienn, która mo»e przechowywa jedynie adresy innych zmiennych, jego deklaracja ró»ni si od deklaracji zwykªych zmiennych. Mo»emy zadeklarowa wska¹nik na ka»dy dost pny w j zyku C typ danych, tj. mo»emy w programie zdeniowa : 196
ˆ wska¹nik na zmienn typu int ˆ wska¹nik na zmienn typu float ˆ wska¹nik na zmienn typu double ˆ wska¹nik na zmienn typu char ˆ wska¹nik na zmienn typu void Ogólny schemat deniowania wska¹nika wygl da nast puj co: 1 typ_danych * nazwa_wskaznika ; Je±li zdeniujemy wska¹nik, który wskazuje na zmienn, wówczas wska¹nik ten przechowuje adres tej zmiennej w pami ci. Je±li wska¹nik nie wskazuje na»adn zmienn, wówczas zamiast adresu wska¹nik przechowuje warto± NULL (wska¹nik nie wskazuje na nic). Cz sto w odniesieniu do wska¹ników, które nie wskazuj na nic, stosuje si równie» okre±lenie pusty wska¹nik. Na poni»szym przykªadzie zdeniowano wska¹niki do wszystkich typów danych j zyka C. W przykªadzie poni»ej wska¹niki nie wskazuj na razie na»adne zmienne, przechowuj wi c warto±ci NULL. 5 /* wskaznik na zmienna typu i n t */ 6 i n t * wi = NULL ; 7 8 /* wskaznik na zmienna typu f l o a t */ 9 f l o a t * wf = NULL ; 10 11 /* wskaznik na zmienna typu double */ 1 double * wd = NULL ; 13 14 /* wskaznik na zmienna typu char */ 15 char * wc = NULL ; 16 17 /* wskaznik na zmienna typu void */ 18 void * wv = NULL ; 19 0 return 0 ; 1 } Nale»y zwróci szczególn uwag na deklarowanie wska¹ników. Gdy chcemy zdeniowa kilka wska¹ników, mo»emy to zrobi na spoosby: 5 /* pierwszy wskaznik na zmienna typu i n t */ 6 i n t * wi1 = NULL ; 7 8 /* drugi wskaznik na zmienna typu i n t */ 9 i n t * wi = NULL ; 10 11 return 0 ; 1 } 197
lub: 5 /* dwa wskazniki na zmienna typu i n t */ 6 7 i n t * wi1 = NULL, * wi = NULL ; 8 9 return 0 ; 10 } Przed ka»d zmienn, któr chcemy zadeklarowa jako zmienn wska¹nikow, MUSI pojawi si symbol gwiazdki, gdy si nie pojawi, zmienna ta b dzie zwykª zmienn, a nie zmienn wska¹nikow : 5 i n t *a, b, *c, d, e, *f, *g, *h ; 6 7 i n t * p, r, s ; 8 9 i n t * x, *y, *z ; 10 11 return 0 ; 1 } W powy»szym kodzie wska¹nikami s : d, e, r, s. a, c, f, g, h, p, x, y, z, natomiast zwykªe zmienne to 9.3 Deniowanie wska¹ników Aby wska¹niki mogªy by u»yteczne w programie, musz wskazywa na zmienne. Nale»y pami ta o tym,»e: ˆ wska¹nik typu int * (na przykªad int *wi) mo»e wskazywa tylko na zmienn typu int (na przykªad int i), ˆ wska¹nik typu float * (na przykªad float *wf) mo»e wskazywa tylko na zmienn typu float (na przykªad float f), ˆ wska¹nik typu double * (na przykªad double *wd) mo»e wskazywa tylko na zmienn typu double (na przykªad double di), ˆ wska¹nik typu char * (na przykªad char *wc) mo»e wskazywa tylko na zmienn typu char (na przykªad char c), Poniewa» wska¹nik mo»e przechowywa jedynie adresy, do wska¹nika nale»y wpisa adres zmiennej, na któr dany wska¹nik powinien wskazywa. Aby to uczyni, konieczne jest skorzystanie z operatorów, przeznaczonych dla wska¹ników. Ze wska¹nikami i adresami zwi zane s dwa operatory: ˆ operator adresu (referencji) & zwracaj cy adres zmiennej podanej po prawej stronie tego operatora ˆ operator wyªuskania (dereferencji) * identykuj cy obszar zmiennej wskazywanej przez wska¹nik podany po prawej stronie tego operatora 198
Adres zmiennej pobieramy za pomoc operatora &. W poni»szym przykªadzie wy±wietlamy warto± zmiennej (tak, jak robili±my to do tej pory), oraz jej adres w pami ci - pobieramy go za pomoc operatora & oraz wykorzystujemy %p jako ci g formatuj cy do wy±wietlenia adresu z pami ci RAM w funkcji printf: 5 /* d e k l a r a c j a zwyklej zmiennej */ 6 i n t a = 104; 7 8 /* w y s w i e t l e n i e w a r t o s c i zmiennej */ 9 printf ( "%d\n", a ) ; 10 11 /* w y s w i e t l e n i e adresu zmiennej */ 1 printf ( "%p\n", &a ) ; 13 14 return 0 ; 15 } Aby wska¹nik wskazywaª na zmienn, nale»y go zadeklarowa i przypisa mu adres zmiennej, na któr ma wskazywa. Skoro adres zmiennej mo»emy pobra za pomoc operatora &, mo»emy tak pobrany adres przypisa do wska¹nika, a nast pnie go wy±wietli : 5 /* d e k l a r a c j a zwyklej zmiennej typu i n t */ 6 i n t a = 104; 7 8 /* w y s w i e t l e n i e w a r t o s c i zmiennej */ 9 printf ( "%d\n", a ) ; 10 /* w y s w i e t l e n i e adresu zmiennej */ 11 printf ( "%p\n", &a ) ; 1 13 /* d e k l a r a c j a wskaznika na zmienna typu i n t */ 14 i n t *w ; 15 16 /* pobranie adresu zmiennej a i wpisanie 17 j e j adresu do wskaznika */ 18 w = &a ; 19 0 /* w y s w i e t l e n i e adresu zmiennej za pomoca wskaznika */ 1 printf ( "%p\n", w ) ; 3 return 0 ; 4 } 199
Za pomoc instrukcji int *w = &a; zadeklarowali±my wska¹nik do zmiennej int oraz wpisalismy do niego adres zmiennej a. Teraz we wska¹niku w znajduje si adres zmiennej a w pami ci RAM dost pnej dla programu. Posiadaj c wska¹nik do zmiennej, mo»emy wy±wietli jej adres, jak równie» sam warto± zmiennej. W poni»szym przykªadzie deklarujemy zmienn zm oraz przypisujemy jej warto±. Nast pnie deklarujemy wska¹nik, który wskazuje na t zmienn, i wy±wietlamy adres zmiennej w pami ci, oraz jej warto± korzystaj c ze wska¹nika. Do pobrania warto±ci zmiennej za pomoc wska¹nika, który na ni wskazuje, u»ywamy operatora *. { 4 /* d e k l a r a c j a zwyklej zmiennej typu f l o a t */ 5 f l o a t zm = 5. 6 ; 6 7 /* wskaznik na zmienna typu f l o a t */ 8 f l o a t *w ; 9 10 /* wskaznik w wskazuje na zmienna zm */ 11 w = &zm ; 1 13 /* w y s w i e t l e n i e adresu zmiennej zm za pomoca wskaznika */ 14 printf ( "%p\n", w ) ; 15 16 /* w y s w i e t l e n i e w a r t o s c i zmiennej zm za pomoca wskaznika */ 17 printf ( "%. f \n", *w ) ; 18 19 return 0 ; 0 } 00
Za pomoc wska¹nika mo»emy zmieni warto± zmiennej. Zaªó»my,»e zmienna abc ma warto± pocz tkow równ 51. Deniujemy wska¹nik wsk który na ni wskazuje, wy±wietlamy warto± zmiennej. Nast pnie, u»ywaj c operatora * w odniesieniu do wska¹nika, wpisujemy do zmiennej abc warto± 4096. Operator * pozwala nam, maj c wska¹nik, dosta si do zmiennej i zmieni / wy±wietli jej warto± : { 4 /* zwykla zmienna typu i n t */ 5 i n t abc = 51; 6 7 /* zmienna wskaznikowa, wskaznik na zmienna typu i n t */ 8 i n t * wsk ; 9 10 /* wskaznik wskazuje t e r a z za zmienna abc */ 11 wsk = &abc ; 1 13 /* wyswietlamy wartosc zmiennej */ 14 printf ( "abc = %d\n", abc ) ; 15 16 /* do zmiennej abc za pomoca wskaznika wpisujemy wartosc 4096 */ 17 /* to samo, co abc = 4096, poniewaz wsk wskazuje na abc */ 18 * wsk = 4096; 19 0 /* wyswietlamy wartosc zmiennej j u z zmieniona */ 1 printf ( "abc = %d\n", abc ) ; 3 return 0 ; 4 } Aby przyswoi sobie deniowanie wska¹ników, prosz przeanalizowa poni»sze przykªady: { 4 /* zmienna typu i n t */ 5 i n t a = 4 ; 6 7 /* wskaznik na typ int, n i e wskazuje na n i c */ 8 i n t * wsk = NULL ; 9 10 /* wskaznik wskazuje na zmienna a */ 11 wsk = &a ; 1 13 /* w y s w i e t l e n i e w a r t o s c i zmiennej a za 14 pomoca wskaznika i samej zmiennej */ 15 printf ( "*wsk = %d, a = %d \n\n", * wsk, a ) ; 16 17 /* w y s w i e t l e n i e adresu zmiennej a za 01
18 pomoca wskaznika i samej zmiennej */ 19 printf ( "wsk = %p, &a = %p \n\n", wsk, &a ) ; 0 1 return 0 ; } 5 /* zmienne typu i n t */ 6 i n t a = 4, b = 5 ; 7 8 /* wskaznik na typ int, n i e wskazuje na n i c */ 9 i n t * wsk = NULL ; 10 11 /* wskaznik wskazuje na zmienna a */ 1 wsk = &a ; 13 14 printf ( "*wsk = %d\n", * wsk ) ; 15 16 /* wskaznik wskazuje na zmienna b */ 17 wsk = &b ; 18 19 printf ( "*wsk = %d\n", * wsk ) ; 0 1 return 0 ; } 0
5 /* zmienna typu i n t */ 6 i n t i = 104; 7 /* wskaznik na typ int, n i e wskazuje na n i c */ 8 i n t * wski = NULL ; 9 /* wskaznik wskazuje t e r a z na zmienna i */ 10 wski = &i ; 11 1 printf ( "* wski = %d \ t \ t i = %d \n", * wski, i ) ; 13 printf ( " wski = %p \ t &i = %p \n\n", wski, &i ) ; 14 15 /* zmienna typu f l o a t */ 16 f l o a t f = 1 0. 4 ; 17 /* wskaznik na typ f l o a t, n i e wskazuje na n i c */ 18 f l o a t * wskf = NULL ; 19 /* wskaznik wskazuje t e r a z na zmienna f */ 0 wskf = &f ; 1 printf ( "* wskf = %f \ t f = %f \n", * wskf, f ) ; 3 printf ( " wskf = %p \ t &f = %p \n\n", wskf, &f ) ; 4 5 /* zmienna typu double */ 6 double d = 1. 0 4 ; 7 /* wskaznik na typ double, n i e wskazuje na n i c */ 8 double * wskd = NULL ; 9 /* wskaznik wskazuje t e r a z na zmienna d */ 30 wskd = &d ; 31 3 printf ( "*wskd = %l f \ t d = %l f \n", * wskd, d ) ; 33 printf ( "wskd = %p \ t &d = %p \n\n", wskd, &d ) ; 34 35 /* zmienna typu char */ 36 char c = 'K ' ; 37 /* wskaznik na typ char, n i e wskazuje na n i c */ 38 char * wskc = NULL ; 39 /* wskaznik wskazuje t e r a z na zmienna c */ 40 wskc = &c ; 41 4 printf ( "*wskc = %c \ t \ t c = %c \n", * wskc, c ) ; 43 printf ( "wskc = %p \ t &c = %p \n\n", wskc, &c ) ; 44 45 return 0 ; 46 } Prosz przyjrze si UWA NIE poni»szemu programowi: 03
5 i n t a = 104; 6 7 i n t * wsk = &a ; 8 9 i n t b ; 10 b = a + 1 ; 11 printf ( "%d\n", b ) ; 1 13 b = * wsk + 1 ; 14 printf ( "%d\n", b ) ; 15 16 return 0 ; 17 } Je±li wsk wskazuje na a, wówczas instrukcje b = *wsk + 1 oraz b = a + 1 s równowa»ne, wykonuj TO SAMO. Gdy deniujemy wska¹nik, który wskazuje na zmienn, sam wska¹nik równie» znajduje si w pewnej komórce pami ci. Rozwa»my poni»szy przykªad: Gdy deklarujemy zmienn a zapisywana jest ona (przykªadowo) do komórki pami ci RAM o adresie 0x0000016. Gdy deklarujemy wska¹nik, który wskazuje na zmienn a, jego warto±ci jest adres zmiennej a w pami ci, czyli adres 0x0000016. Jednak, aby wska¹nik byª dost pny i mógª by u»ywany w programie, on równie» musi mie swoje miejsce w pami ci RAM. Dla powy»szego obrazka: wska¹nik wsk1 znajduje si w komórce pami ci RAM o adresie 0x0000014. W j zyku C mo»liwe jest zdeniowanie wska¹nika, kótry b dzie wskazywaª na inny wska¹nik. Poniewa» wska¹nik równie» ma swój wªasny adres w pami ci, mo»emy zdeniowa wska¹nik, który b dzie na niego wskazywaª: 04
Na obrazku powy»ej widzimy zmienn a, na któr wskazuje wska¹nik wsk1, na który wskazuje wska¹nik wsk. Wówczas wsk = 0x0000014, wsk1 = 0x0000016, a = 18 - wska¹nik wsk przechowuje adres w pami ci wska¹nika wsk1, wska¹nik wsk1 przechowuje adres w pami ci zmiennej a. W j zyku C taka konstrukcja b dzie wygl da nast puj co (oczywi±cie adresy b d si ró»ni, powy»sze to tylko przykªad): 5 /* zwykla zmienna typu i n t */ 6 i n t a = 18; 7 8 /* wskaznik na typ int, n i e wskazuje na n i c */ 9 i n t * wsk1 = NULL ; 10 11 /* wskaznik wsk wskazuje na zmienna a */ 1 wsk1 = &a ; 13 14 /* wskaznik na wskaznik na zmienna int, n i e 15 wskazuje na n i c */ 16 i n t ** wsk = NULL ; 17 18 /* wskaznik wsk wskazuje na wskaznik wsk1 */ 19 wsk = &wsk1 ; 0 1 printf ( "Adres zmiennej a : \ t \ t \ t \ t %p \n", &a ) ; printf ( "Wartosc zmiennej a : \ t \ t \ t \ t %d \n\n", a ) ; 3 4 printf ( "Adres wskaznika wsk1 : \ t \ t \ t \ t %p \n", &wsk1 ) ; 5 printf ( "Wskaznik wsk1 wskazuje na adres : \ t \ t %p \n", wsk1 ) ; 6 printf ( " Wartosc pod adresem %p j e s t rowna \ t %d \n\n", wsk1, * wsk1 ) ; 7 8 printf ( "Adres wskaznika wsk : \ t \ t \ t \ t %p \n", &wsk ) ; 9 printf ( "Wskaznik wsk wskazuje na adres : \ t \ t %p \n", wsk ) ; 30 printf ( " Wartosc pod adresem %p j e s t rowna \ t %p \n\n", wsk, * wsk ) ; 31 3 return 0 ; 33 } Po skompilowaniu i uruchomieniu powy»szego programu zobaczymy: 05
Na obrazku / schemacie taki rozkªad adresów mo»na przedstawi nast puj co: Mo»na powiedzie,»e wska¹nik wsk1 wskazuje na zmienn a bezpo±rednio, natomiast wska¹nik wsk wskazuje na zmienn a po±rednio, za pomoc wska¹nika wsk1. Oczywi±cie mo»liwe jest zdeniowanie wska¹nika, który wskazuje na wska¹nik, wskazuj cy na wska¹nik, itd.... Mo»liwe jest równie» pobranie warto±ci zmiennej a za pomoc wska¹nika, który wskazuje na wska¹nik na zmienn a. 5 /* zwykla zmienna typu i n t */ 6 i n t a = 18; 7 8 /* wskaznik na typ int, n i e wskazuje na n i c */ 9 i n t * wsk1 = NULL ; 10 11 /* wskaznik wsk wskazuje na zmienna a */ 1 wsk1 = &a ; 13 14 /* wskaznik na wskaznik na zmienna int, n i e 15 wskazuje na n i c */ 16 i n t ** wsk = NULL ; 17 18 /* wskaznik wsk wskazuje na wskaznik wsk1 */ 19 wsk = &wsk1 ; 0 1 printf ( "Wartosc zmiennej a j e s t rowna : \ t %d \n\n", ** wsk ) ; 3 return 0 ; 4 } 06
9.4 Operacje arytmetyczne na wska¹nikach Na wska¹nikach mog by wykonywane nast puj ce operacje: 1. przypisania: =. porównania: >, <, >=, <=, ==,!= 3. operacje powi kszania lub pomniejszania wska¹nika (+, -, ++, --, +=, -=) o liczb caªkowit (tylko dla wska¹ników zdeniowanych, czyli wskazuj cych ju» na co±) 4. operacje odejmowania wska¹ników tego samego typu - wyznaczenie odlegªo±ci pomi dzy dwoma adresami w pami ci Poni»ej przeanalizujemy i omówimy ka»d z mo»liwych operacji dost pnych dla wska¹ników. 1. Poniewa» wska¹niki przechowuj adresy, istnieje mo»liwo± przypisania jednej zmiennej adresowej do innej zmiennej adresowej. Oznacza to,»e je±li do jakiego± wska¹nika, np. wska¹nika wsk przypiszemy inny wska¹nik, np. wska¹nik wsk1, b dzie to oznacza, i» od tej pory wska¹nik wsk wskazuje na to samo, na co wskazuje wska¹nik wsk1 (oba wska¹niki przechowuj TEN SAM ADRES): 5 /* zwykla zmienna typu i n t */ 6 i n t a = 51; 7 8 /* wskazniki na zmienne typu i n t */ 9 i n t * wsk1, * wsk ; 10 11 /* wskaznik wsk1 wskazuje na zmienna a */ 1 wsk1 = &a ; 13 14 /* wskaznik wsk wskazuje na to samo, 15 co wskaznik wsk1, c z y l i wskaznik 16 wsk wskazuje t e r a z na zmienna a */ 17 wsk = wsk1 ; 18 19 printf ( "*wsk1 = %d \ t \ t *wsk = %d \n\n", * wsk1, * wsk ) ; 0 printf ( " wsk1 = %p \ t wsk = %p \n\n", wsk1, wsk ) ; 1 return 0 ; 3 } 07
. Dost pne operatory porównania dla wska¹ników pozwalaj nam sprawdzi, czy dwa wska¹niki wskazuj te same zmienne (==), czy wskazuj na inne zmienne (!=), czy zmienna, na któr wskazuje jeden wska¹nik znajduje si w pami ci RAM wcze±niej / pó¹niej ni» inna zmienna, na któr wskazuje inny wska¹nik (>, <, >=, <=). Na poni»szym listingu zostaªy zaprezentowane przykªadowe wykorzystania operatorów porówania dla wska¹ników: 5 /* zwykla zmienna typu i n t */ 6 i n t a = 51; 7 /* zwykla zmienna typu i n t */ 8 i n t b = 51; 9 10 /* wskazniki na zmienne typu i n t */ 11 i n t * wsk1, * wsk ; 1 13 /* wskaznik wsk1 wskazuje na zmienna a */ 14 wsk1 = &a ; 15 16 /* wskaznik wsk wskazuje na zmienna a */ 17 /* to samo mozna uzyskac p i s z a c : wsk = wsk1 */ 18 wsk = &a ; 19 0 /* spr czy oba wskazniki wskazuja na t e sama zmienna */ 1 i f ( wsk1 == wsk ) printf ( "Oba wskazniki wskazuja na t e sama zmienna \n\n" ) ; 3 4 /* t e r a z wskaznik wsk wskazuje na zmienna b */ 5 wsk = &b ; 6 7 /* spr czy oba wskazniki wskazuja na rozne zmienne */ 8 i f ( wsk1!= wsk ) 9 printf ( " Wskazniki wskazuja na rozne zmienne \n\n" ) ; 30 31 i f ( wsk1 < wsk ) 3 printf ( "Zmienna b j e s t d a l e j w pamieci n i z zmienna a \n\n" ) ; 33 i f ( wsk1 <= wsk ) 34 printf ( "Zmienna b j e s t d a l e j w pamieci n i z zmienna a \n\n" ) ; 35 36 printf ( "Sprawdzmy to, porownujac i c h adresy w RAMie: \ n\n" ) ; 37 38 printf ( "wsk1 = %p \ t &a = %p \n", wsk1, &a ) ; 39 printf ( "wsk = %p \ t &b = %p \n", wsk, &b ) ; 40 41 return 0 ; 4 } 08
3. Powi kszenie (pomniejszenie) wska¹nika o warto± N powoduje wyznaczenie adresu przesuni tego o N * sizeof(typ_zmiennej_wskazywanej) bajtów w kierunku rosn cych adresów, tj.: Dla przykªadu powy»ej: deniuj c wska¹nik na typ danych int wiemy, i» int zajmuje 4 bajty w pami ci RAM (mo»emy to sprawdzi za pomoc operatora sizeof(int) pisz c np.: printf("%d\n", sizeof(int));). Na powy»szym obrazku widzimy, iz na pocz tku wska¹nik w przechowywaª adres 36. Je±li zwi kszymy wska¹nik o N=1, czyli wykonamy instrukcj w ++ (lub w = w + 1, albo w += 1), wówczas zwi kszymy adres wska¹nika o 1 * 4 (skoro N = 1, a sizeof(int) = 4) który b dzie przechowywaª wówczas warto± 40 (36 + 1 * 4 = 40). Sytuacja wygl da analogicznie w przypadku pomniejszania warto±ci wska¹nika. 5 /* zwykla zmienna typu i n t */ 6 i n t a = 51; 7 8 /* wskaznik na typ int, n i e wskazuje na n i c */ 9 i n t * wsk = NULL ; 10 11 /* wskaznik wskazuje na zmienna a */ 1 wsk = &a ; 13 14 printf ( " Aktualny adres zapisany we wskazniku : \ t \ t \ t %p \n", wsk ) ; 15 printf ( "Wartosc zmiennej wskazywanej przez wskaznik : \ t \ t %d \n", * wsk ) ; 16 printf ( " Aktualny adres zmiennej a : \ t \ t \ t \ t %p \n\n", &a ) ; 17 18 /* zwiekszamy wskaznik o 1, przesuwamy adresy */ 19 /* t e r a z wskaznik n i e wskazuje juz na zmienna a, 0 t y l k o na inny adres w pamieci */ 1 wsk += 1 ; 3 printf ( " Aktualny adres zapisany we wskazniku : \ t \ t \ t %p \n", wsk ) ; 4 printf ( "Wartosc zmiennej wskazywanej przez wskaznik : \ t \ t %d \n", * wsk ) ; 5 printf ( " Aktualny adres zmiennej a : \ t \ t \ t \ t %p \n\n", &a ) ; 6 7 /* zmniejszamy wskaznik o 1, przesuwamy adresy */ 09
8 /* t e r a z wskaznik wskazuje juz na zmienna a */ 9 wsk = wsk 1 ; 30 31 printf ( " Aktualny adres zapisany we wskazniku : \ t \ t \ t %p \n", wsk ) ; 3 printf ( "Wartosc zmiennej wskazywanej przez wskaznik : \ t \ t %d \n", * wsk ) ; 33 printf ( " Aktualny adres zmiennej a : \ t \ t \ t \ t %p \n\n", &a ) ; 34 35 return 0 ; 36 } 4. Mo»liwe jest równie» sprawdzenie odlegªo±ci w pami ci RAM dwóch zmiennych - wykonujemy to równie» za pomoc wska¹ników, dzi ki którym mamy dost p do adresów zmiennych: 5 /* zwykla zmienna typu i n t */ 6 i n t a = 5. 1 ; 7 8 /* wskazniki na typ int, n i e wskazuja na n i c */ 9 i n t * wsk1 = NULL, * wsk = NULL ; 10 11 /* wskaznik wsk1 wskazuje na zmienna a */ 1 wsk1 = &a ; 13 14 wsk = wsk1 + ; 15 16 printf ( " wsk1 = %p \ t wsk = %p \n\n", wsk1, wsk ) ; 17 printf ( " wsk wsk1 = %d \n", wsk wsk1 ) ; 18 19 return 0 ; 0 } 10
9.5 Wska¹nik na staª Wska¹nik na staª, jak sama nazwa wskazuje, pokazuje na staª, której nie mo»emy zmieni. Mo»emy jednak zmieni to, na co wskazuje wska¹nik (adres). Nie mo»emy zmieni warto±ci zmiennej, na któr wskazuje wska¹nik. Istniej dwa sposoby deniowania wska¹ników na staªe: 1 const f l o a t * ptr = &zmienna ; f l o a t const * ptr = &zmienna ; Przykªad 9.5.1. Nie mo»emy zmieni warto±ci, na któr wskazuje wska¹nik, je±li zechcemy to zrobi (linia 5) kompilator poinformuje nas o bª dzie. W poni»szym przykªadzie chcemy zmieni warto± zmiennej j z 666 na 45 - nie powiedzie sie to z wy»ej opisanych powodów: 3 i n t main ( i n t argc, char ** argv ) 5 // deklarujemy i definiujemy zmienna 6 const i n t i = 13, j = 666; 7 printf ( "zmienna = %d\n", i ) ; 8 9 // wskaznik wskazuje na s t a l a i 10 const i n t * ptr1 = &i ; 11 1 // wskaznik wskazuje na s t a l a i 13 i n t const * ptr = &i ; 14 15 printf ( "* ptr1 = %d, * ptr = %d\n", * ptr1, * ptr ) ; 16 17 // wskaznik moze wskazywac na inna s t a l a... 18 ptr = &j ; 19 0 printf ( "* ptr1 = %d, * ptr = %d\n", * ptr1, * ptr ) ; 1 //... a l e n i e mozemy zmienic wartosci, 3 // na ktora wskaznik wskazuje BLAD 4 // tu chcemy zmienic wartosc j z 666 na 45 5 * ptr = 4 5 ; 6 7 return 0 ; 8 } 9.6 Staªy wska¹nik Podobnie jak mo»emy deklarowa zwykªe staªe, tak samo mo»emy mie staªe wska¹niki. Staªy wska¹nik to taki, którego nie mo»na przestawi na inny adres. To wska¹nik, który ZAWSZE wskazuje na to samo 11
(na ten sam adres). Nie mo»emy zmieni adresu przechowywanego przez wska¹nik (wska¹nik zawsze b dzie wskazywaª na to samo, na t sam zmienn ), jednak mo»emy zmieni warto±c zmiennej, na któr ten wska¹nik wskazuje. Istniej dwa sposoby deniowania staªych wska¹ników: 1 i n t * const p = &zmienna ; const i n t * const *p = &zmienna ; Przykªad 9.6.1. Kiedy spróbujemy zmieni adres wska¹nika (kiedy chcemy, aby wskazywaª na inn zmienn ), kompilator zawiadomi nas o bª dzie. 3 i n t main ( i n t argc, char ** argv ) 5 // deklarujemy i definiujemy zmienna 6 i n t i = 13; 7 printf ( "zmienna = %d\n", i ) ; 8 9 // deklarujemy i definiujemy wskaznik, 10 // ktory wskazuje na t e zmienna 11 i n t * wsk = &i ; 1 printf ( "*wsk = %d\n", * wsk ) ; 13 14 // s t a l y wskaznik, wskazuje na zmienna 15 i n t * const p = &i ; 16 // to samo, co powyzej, s t a l y wskaznik 17 const i n t * const *r = &i ; 18 19 i n t j = 987; 0 1 // BLAD wskaznik p wskazuje j u z na zmienna i // wiec n i e moze wskazywac na zmienna j 3 p = &j ; 4 5 // mozemy zmienic wartosc, na ktora 6 // wskazuje wskaznik 7 *p = 1040; 8 printf ( "zmienna = %d\n", i ) ; 9 30 return 0 ; 31 } 9.7 Operator sizeof Zanim nauczymy si tworzy dynamiczne tablice jednowymiarowe, musimy najpierw pozna dziaªanie operatora sizeof. Operator sizeof inaczej nazywany jest operatorem rozmiaru - operator ten dost pny jest w j zyku C i C++. Operatora u»ywamy podobnie jak funkcji, która jako argument pobiera (w naszym przypadku) nazw typu danych, którego rozmiar chcemy pozna, oraz zwraca jego rozmiar w bajtach. Przykªadowe u»ycie operatora zaprezentowano poni»ej: 1
5 i n t rozmiar = s i z e o f ( i n t ) ; 6 printf ( " Int zajmuje %d bajty w pamieci RAM\n", rozmiar ) ; 7 return 0 ; 8 } 9.8 Zwi zek pomi dzy wska¹nikiem a tablic Nale»y zapami ta, i» NAZWA TABLICY JEST WSKA NIKIEM DO PIERWSZEGO JEJ ELEMNETU. Oznacza to,»e mo»emy odnie± si do pierwszego elementu tablicy na dwa sposoby: ˆ za pomoc nazwy tablicy i indeksu o warto±ci 0 ˆ poprzez ten wska¹nik (sam nazw tablicy) W poni»szym kodzie korzystamy z tej wªasno±ci - skoro sama nzwa tablicy jest wska¹nikiem (do jej pierwszego elementu, elementu z pod indeksu 0), musimy do pobrania warto±ci ze wska¹nika u»y symbolu * (tak, jak robili±my to podczas pobierania warto±ci z danego adresu zapisanego we wska¹niku): 5 6 i n t tab [ ] = {104, 56, 384, 4096, 51}; 7 8 // odwolanie s i e za pomoca indeksu 9 printf ( "%d\n", tab [ 0 ] ) ; 10 11 // odwolanie s i e za pomoca wskaznika ( samej nazwy t a b l i c y ) 1 printf ( "%d\n", * tab ) ; 13 14 return 0 ; 15 } St d, tab[0] == *tab. Skoro tab (sama nazwa tablicy) jest wska¹nikiem na jej pierwszy element (czyli na element tab[0]) do wy±wietlenia warto±ci elementu musimy u»y symbolu *, który pozwala nam pobra warto± pod wskazanym adresem. Dlatego te» instrukcja printf("%d ",*tab); pokazuje warto± 13
pierwszego elementu tablicy (je±li tab to adres, wówczas *tab daje nam warto± z pod tego adresu): Mo»emy równie» wy±wietli adres pierwszego elementu tablicy, za pomoc samej jej nazwy (czyli wska¹nika, który przechowuje adres pierwszego jej elementu), lub pobieraj c adres zmiennej z tablicy z pod indeksu 0, u»ywaj c operatora pobrania adresu, &: 5 i n t n = 5 ; 6 i n t i ; 7 8 i n t tab [ ] = {104, 51, 56, 18}; 9 10 printf ( "%p\n", tab ) ; 11 printf ( "%p\n", &tab [ 0 ] ) ; 1 13 return 0 ; 14 } Poniewa» tablica w pami ci jest ci gªym obszarem, w którym kolejne elementy umieszczane s jeden po drugim, znaj c adres pierwszego elementu, mo»emy za pomoc wska¹nika uzyska dost p do jej kolejnych elementów. Pami tamy,»e na wska¹nikach mo»emy wykonywa operacj dodawania warto±ci do zmiennej wska¹nikowej, czyli przesuni cia wska¹nika na inny adres. Warto zauwa»y,»e w przypadku 14
elementów, które zajmuj np. 4 bajty pami ci (liczby typu int), b dziemy przy ka»dym zwi kszeniu wska¹nika przeskakiwa o te cztery bajty,»eby dosta si do nast pnej warto±ci tablicy. W przypadku tablicy typu char przeskok b dzie o jeden bajt - tyle ile zajmuje typ char. Reguªa ta dotyczy ka»dego innego typu. W poni»szym przykªadzie zadeklarowali±my tablic o elementach typu int. Za pomoc wska¹nika wy±iwetlamy adres jej pierwszego elementu. Aby wy±wietli adres kolejnego elementu do wska¹nika musimy doda warto± 1, czyli przesun wska¹nik na kolejny element tablicy: 5 i n t n = 5 ; 6 i n t i ; 7 8 i n t tab [ ] = {104, 51, 56, 18}; 9 10 /* wyswietlamy adres 1 go elementu t a b l i c y, elementu 104 */ 11 printf ( "%p\n", tab ) ; 1 printf ( "%p\n", &tab [ 0 ] ) ; 13 14 /* przesuwamy wskaznik o 1 miejsce, c z y l i dla i n t a 4 bajty, 15 na element nastepny, ktorym j e s t 51 i wyswietlamy 16 adres go elementu t a b l i c y */ 17 printf ( "%p\n", tab+1) ; 18 printf ( "%p\n", &tab [ 1 ] ) ; 19 0 return 0 ; 1 } Poniewa» pod kolejnymi wska¹nikami kryj si elemnety tablicy, istnieje mo»liwo± pobrania ich warto±ci przy u»yciu operatora wyªuskania, czyli *: 15
5 i n t n = 5 ; 6 i n t i ; 7 8 i n t tab [ ] = {104, 51, 56, 18}; 9 10 /* wyswietlamy wartosc 1 go elementu t a b l i c y, elementu 104 */ 11 printf ( "%d\n", * tab ) ; 1 printf ( "%d\n", tab [ 0 ] ) ; 13 14 /* przesuwamy wskaznik o 1 miejsce, c z y l i dla i n t a 4 bajty, 15 na element nastepny, ktorym j e s t 51 i wyswietlamy 16 wartosc go elementu t a b l i c y */ 17 printf ( "%d\n", *( tab+1) ) ; 18 printf ( "%d\n", tab [ 1 ] ) ; 19 0 return 0 ; 1 } Pisz c ogólnie, dla dowolnej tablicy: wska¹niki tab tab+1 tab+... tab+n-1 dane element pierwszy element drugi element trzeci... element n-1 indeksy 0 1... n-1 Dzi ki temu, i» sama nazwa tablicy jest wska¹nikiem do 1-go jej elementu, mo»emy za pomoc przesuwania wska¹nika wy±wietli wszystkie elementy tablicy: 3 4 i n t main ( ) 5 { 6 i n t tab [ 1 0 ] = {1,,4, 5, 6, 4, 3,, 1, 0 } ; 7 i n t i ; 8 9 f o r ( i=0; i <10; i++) 16
10 printf ( "%d ", *( tab+i ) ) ; 11 1 printf ( "\n" ) ; 13 14 f o r ( i=0; i <10; i++) 15 printf ( "%d ", tab [ i ] ) ; 16 17 return 0 ; 18 } Poni»sze kody pokazuj ró»ne mo»liwe sposoby dost pu do elementów tablicy: ˆ Za pomoc operatora indeksu: 5 i n t tab [ 4 ] ; 6 i n t n = 4 ; 7 i n t i ; 8 9 f o r ( i=0;i<n ; i++) 10 { 11 scanf ( "%d", &tab [ i ] ) ; 1 } 13 14 f o r ( i=0;i<n ; i++) 15 { 16 tab [ i ] *= ; 17 } 18 19 f o r ( i=0;i<n ; i++) 0 { 1 printf ( "%d ", tab [ i ] ) ; } 3 4 return 0 ; 5 } ˆ Za pomoc indeksu i operatora wyªuskania: 5 i n t tab [ 4 ] ; 6 i n t n = 4 ; 7 i n t i ; 17
8 9 f o r ( i=0;i<n ; i++) 10 { 11 scanf ( "%d", ( tab+i ) ) ; 1 } 13 14 f o r ( i=0;i<n ; i++) 15 { 16 *( tab+i ) *= ; 17 /* albo : */ 18 /* *( tab+i ) = * *( tab+i ) */ 19 } 0 1 f o r ( i=0;i<n ; i++) { 3 printf ( "%d ", *( tab+i ) ) ; 4 } 5 6 return 0 ; 7 } ˆ Za pomoc wska¹nika i operatora wyªuskania: 5 i n t tab [ 4 ] ; 6 i n t n = 4 ; 7 i n t i ; 8 i n t * wsk = NULL ; 9 10 f o r ( i=0, wsk=tab ; i<n ; i++, wsk++) 11 { 1 scanf ( "%d", wsk ) ; 13 } 14 15 f o r ( i=0, wsk=tab ; i<n ; i++, wsk++) 16 { 17 * wsk *= ; 18 /* albo : */ 19 /* *wsk = * *wsk */ 0 } 1 f o r ( i=0, wsk=tab ; i<n ; i++, wsk++) 3 { 4 printf ( "%d ", * wsk ) ; 5 } 6 7 return 0 ; 8 } ˆ Za pomoc samych wska¹ników: (&tab[rozmiar] oraz tab+n oznaczaj adres ko«ca tablicy, ostatniego jej elementu, dzi ki czemu wska¹nik nie wyjdzie poza tablic ) 5 i n t tab [ 4 ] ; 6 i n t n = 4 ; 7 i n t i ; 8 i n t * wsk = NULL ; 9 10 f o r ( wsk=tab ; wsk < tab + n ; wsk++) 11 { 18
1 scanf ( "%d", wsk ) ; 13 } 14 15 f o r ( wsk=tab ; wsk < tab + n ; wsk++) 16 { 17 * wsk *= ; 18 /* albo : */ 19 /* *wsk = * *wsk */ 0 } 1 f o r ( wsk=tab ; wsk < tab + n ; wsk++) 3 { 4 printf ( "%d ", * wsk ) ; 5 } 6 7 return 0 ; 8 } 5 i n t tab [ 4 ] ; 6 i n t n = 4 ; 7 i n t i ; 8 i n t * wsk = NULL ; 9 10 f o r ( wsk=tab ; wsk < &tab [ n ] ; wsk++) 11 { 1 scanf ( "%d", wsk ) ; 13 } 14 15 f o r ( wsk=tab ; wsk < &tab [ n ] ; wsk++) 16 { 17 * wsk *= ; 18 /* albo : */ 19 /* *wsk = * *wsk */ 0 } 1 f o r ( wsk=tab ; wsk < &tab [ n ] ; wsk++) 3 { 4 printf ( "%d ", * wsk ) ; 5 } 6 7 return 0 ; 8 } 9.9 Wska¹niki i dynamiczne tablice jednowymiarowe Tablica dynamiczna (ang. dynamic array) jest tworzona w czasie uruchomienia programu. Jej rozmiar mo»e by wyliczany. Co wi cej, gdy przestanie by potrzebna mo»emy j usun z pami ci. Dzi ki tym wªasno±ciom program efektywniej wykorzystuje zasoby pami ciowe komputera. Aby utworzy tablic dynamiczn jednowymiarow liczb caªkowitych o rozmiarze (ilo±ci elementów) równej n, nale»y: 1. Zdeniowa zmienn wska¹nikow, która b dzie przechowywaªa adres pierwszego elementu tablicy. Jest to zwykªa denicja wska¹nika: 1 i n t * wsk ;. Przydzieli obszar pami ci dla tablicy. Nale»y zastosowa nast puj c konstrukcj : 1 i n t * tab = malloc ( n * s i z e o f ( i n t ) ) ; 19
Poniewa» nasza tablica ma przechowywa n elementów, a ka»dy z jej elementów ma by zmienn typu int, nale»y sprawdzi, ile bajtów w pami ci zajmuje int, a nast pni warto± t pomno-»y przez ilo± elementów tablicy - dzi ki temu przydzielimy pami na n elementów typu int - utworzymy tablic n-elementow liczb caªkowitych. 3. Do elementów tak utworzonej tablicy odwoªujemy si poprzez ich indeksy, na przykªad: 1 wsk [ 0 ], wsk [ 1 ], wsk [ ],... ogólnie: wsk[indeks], gdzie indeks - powinien by w zakresie od 0 do liczba_elementów - 1. Kompilator nie sprawdza, czy element o danym indeksie znajduje si faktycznie w tablicy. Nale»y zachowa ostro»no±, aby nie wyj± poza tablic. 4. Gdy tablica dynamiczna przestanie by potrzebna, usuwamy j z pami ci za pomoc instrukcji: 1 free ( wsk ) ; wsk = NULL ; Po tej operacji obszar pami ci zaj ty przez tablic zostaje zwrócony do systemu. Wska¹nik mo»na wykorzysta ponownie do innej tablicy wg powy»szych punktów. Tablicy dynamicznej u»ywamy tak samo jak zwykª tablice statyczn, nie trzeba operowa wska¹nikami (ale mo»na), wska¹niki potrzebne s przy jej deklaracji. 9.9.1 Tworzenie dynamicznej tablicy jednowymiarowej Przeanalizujmy poni»szy przykªad. Zaªó»my,»e chcemy stworzy dynamiczn, jednowymiarow tablic o ilo±ci elementów (rozmiarze) podanym przez u»ytkownika. Aby to zrobi, na pocz tku wczytujemy rozmiar tablicy, nast pnie przydzielamy dla niej pami... : 1 i n t n ; scanf ( "%d", &n ) ; 3 i n t * wski = NULL ; 4 wski = ( i n t *) malloc ( n * s i z e o f ( i n t ) ) ; 9.9. Dost p do elementów dynamicznej tablicy jednowymiarowej... zerujemy tablic, wy±wietlamy jej zawarto± (czyli uzyskujemy dost p do elmentów tablicy): 1 f o r ( i=0; i<n ; i++) wski [ i ] = 0 ; 3 4 f o r ( i=0; i<n ; i++) 5 printf ( "%d ", wski [ i ] ) ; 6 printf ( "\n" ) ; 9.9.3 Usuwanie dynamicznej tablicy jednowymiarowej... i usuwamy przydzielon pami : 1 free ( wski ) ; wski = wski ; 0
9.9.4 Tworzenie, dost p do elementów oraz usuwanie dynamicznej tablicy jednowymiarowej Wszystkie powy»sze operacje, aby mogªy dziaªa jako program, zostaªy zebrane na listingu poni»ej. Zaprezentowany kod: 1. Wczytuje od u»ytkownika ilo± elementów tablicy. Deniuje wska¹niki na typy danych int, float, double, char 3. Korzystaj c ze wska¹ników przydziela pami dla ka»dej tablicy (dla tablicy elementów typu int, float, double, char) 4. Wypeªnia ka»d z tablic 5. Wy±wietla ka»d z tablic 6. Zwalnia pami na ka»d z przydzielonych tablic Podsumowuj c, na poni»szych przykªadach widzimy schematy tworzenia dynamicznych tablic jednowymiarowych o n elementach, dla ka»dego typu danych: 5 i n t i ; 6 7 /* i l o s c elementow t a b l i c y */ 8 i n t n ; 9 /* wczytanie i l o s c i elementow t a b l i c y */ 10 printf ( "Podaj i l o s c elementow t a b l i c y : \ n> " ) ; 11 scanf ( "%d", &n ) ; 1 13 /* wskaznik na zmienna typu i n t */ 14 i n t * wski = NULL ; 15 16 /* p r z y d z i e l e n i e pamieci na n elemenotwa t a b l i c e intow */ 17 wski = ( i n t *) malloc ( n * s i z e o f ( i n t ) ) ; 18 19 /* wyzerowanie t a b l i c y dynamicznej */ 0 f o r ( i=0; i<n ; i++) 1 wski [ i ] = 0 ; 3 /* w y s w i e t l e n i e t a b l i c y */ 4 f o r ( i=0; i<n ; i++) 5 printf ( "%d ", wski [ i ] ) ; 6 printf ( "\n" ) ; 7 8 /* z w o l n i e n i e pamieci p r z y d z i e l o n e j na t a b l i c e */ 9 free ( wski ) ; 30 wski = NULL ; 31 3 return 0 ; 33 } 5 i n t i ; 6 1
7 /* i l o s c elementow t a b l i c y */ 8 i n t n ; 9 /* wczytanie i l o s c i elementow t a b l i c y */ 10 printf ( "Podaj i l o s c elementow t a b l i c y : \ n> " ) ; 11 scanf ( "%d", &n ) ; 1 13 /* wskaznik na zmienna typu f l o a t */ 14 f l o a t * wsk = NULL ; 15 16 /* p r z y d z i e l e n i e pamieci na n elemenotwa t a b l i c e f l o a t */ 17 wsk = ( f l o a t *) malloc ( n * s i z e o f ( f l o a t ) ) ; 18 19 /* wyzerowanie t a b l i c y dynamicznej */ 0 f o r ( i=0; i<n ; i++) 1 wsk [ i ] = 0 ; 3 /* w y s w i e t l e n i e t a b l i c y */ 4 f o r ( i=0; i<n ; i++) 5 printf ( "%.1 f ", wsk [ i ] ) ; 6 printf ( "\n" ) ; 7 8 /* z w o l n i e n i e pamieci p r z y d z i e l o n e j na t a b l i c e */ 9 free ( wsk ) ; 30 wsk = NULL ; 31 3 return 0 ; 33 } 5 i n t i ; 6 7 /* i l o s c elementow t a b l i c y */ 8 i n t n ; 9 /* wczytanie i l o s c i elementow t a b l i c y */ 10 printf ( "Podaj i l o s c elementow t a b l i c y : \ n> " ) ; 11 scanf ( "%d", &n ) ; 1 13 /* wskaznik na zmienna typu double */ 14 double * wsk = NULL ; 15 16 /* p r z y d z i e l e n i e pamieci na n elemenotwa t a b l i c e double */ 17 wsk = ( double *) malloc ( n * s i z e o f ( double ) ) ; 18 19 /* wyzerowanie t a b l i c y dynamicznej */ 0 f o r ( i=0; i<n ; i++) 1 wsk [ i ] = 0 ; 3 /* w y s w i e t l e n i e t a b l i c y */ 4 f o r ( i=0; i<n ; i++) 5 printf ( "%.1 f ", wsk [ i ] ) ; 6 printf ( "\n" ) ; 7 8 /* z w o l n i e n i e pamieci p r z y d z i e l o n e j na t a b l i c e */ 9 free ( wsk ) ; 30 wsk = NULL ; 31 3 return 0 ; 33 }
5 i n t i ; 6 7 /* i l o s c elementow t a b l i c y */ 8 i n t n ; 9 /* wczytanie i l o s c i elementow t a b l i c y */ 10 printf ( "Podaj i l o s c elementow t a b l i c y : \ n> " ) ; 11 scanf ( "%d", &n ) ; 1 13 /* wskaznik na zmienna typu char */ 14 char * wsk = NULL ; 15 16 /* p r z y d z i e l e n i e pamieci na n elemenotwa t a b l i c e char */ 17 wsk = ( char *) malloc ( n * s i z e o f ( char ) ) ; 18 19 /* wyzerowanie t a b l i c y dynamicznej */ 0 f o r ( i=0; i<n ; i++) 1 wsk [ i ] = ' 0 ' ; 3 /* w y s w i e t l e n i e t a b l i c y */ 4 f o r ( i=0; i<n ; i++) 5 printf ( "%c ", wsk [ i ] ) ; 6 printf ( "\n" ) ; 7 8 /* z w o l n i e n i e pamieci p r z y d z i e l o n e j na t a b l i c e */ 9 free ( wsk ) ; 30 wsk = NULL ; 31 3 return 0 ; 33 } 9.10 Wska¹niki i funkcje Wska¹niki mog by argumentami funkcij. Je±li zechcemy napisa funkcj, która jako argument pobiera wska¹nik na zmienn caªkowit, i nie zwraca nic, jej nagªówek b dzie wygl daª nast puj co: void funkcja(int *wsk); Gdy funkcja pobiera wska¹nik do pierwszego elementu dynamicznie utworzonej jednowymiarowej tablicy (mówimy wówczas,»e funkcja pobiera jako argument dynamiczn tablic ), musimy jako kolejny argument poda ilo± jej elementów. Nagªówek takiej funkcji wygl da wi c nast puj co (funkcja nic nie zwraca): void fun(int *tab, int n);. Funkcja mo»e równie» zwraca wska¹nik (np. do pierwszego elementu utworzonej tablicy). Je±li w funkcji za pomoc wska¹ników tworzymy dynamiczn tablic elementów typu int a nast pnie zwracamy wska¹nik do pierwszego elementu, nagªówek funkcji b dzie wygl da nast puj co: int * fun(); (funkcja nie pobiera argumentów). Nale»y pami ta o tym, i» poniewa» wska¹nik ma dost p do oryginalnej zmiennej (za pomoc jej adresu), je±li do funkcji jako argument przeka»emy wska¹nik, który wskazuje na zmienn, a nast pnie za pomoc wska¹nika w funkcji zmodykujemy t zmienn, zmiana b dzie widoczna równie» po wyj±ciu z funkcji. Ta sama zasada obowi zuje podczas przekazywanie dynamicznych tablic (wska¹nika na pierwszy element tablicy). Prosz przeanalizowa poni»sze przykªady: #i n c l u d e <s t d l i b. h> 3 #i n c l u d e <s t r i n g. h> 4 5 void przypisz ( i n t *w ) 6 { 7 *w = 104; 3
8 } 9 10 i n t main ( ) 11 { 1 i n t a = 5 ; 13 i n t * wsk = &a ; 14 15 printf ( "PRZED FUNKCJA: \ n\n" ) ; 16 printf ( "a = %d *wsk = %d\n", a, * wsk ) ; 17 18 przypisz ( wsk ) ; 19 0 printf ( "\n" ) ; 1 printf ( "PO FUNKCJI: \ n\n" ) ; printf ( "a = %d *wsk = %d\n", a, * wsk ) ; 3 4 return 0 ; 5 } #i n c l u d e <s t d l i b. h> 3 #i n c l u d e <s t r i n g. h> 4 5 void zamien ( i n t * tab, i n t n ) 6 { 7 i n t i ; 8 f o r ( i=0; i<n ; i++) 9 tab [ i ] = 1000; 10 } 11 1 i n t main ( ) 13 { 14 i n t i ; 15 i n t *w = ( i n t *) malloc ( s i z e o f ( i n t ) * 4) ; 16 17 f o r ( i=0; i<4; i++) 18 w [ i ] = i + 1 ; 19 0 printf ( "PRZED FUNKCJA: \ n\n" ) ; 1 f o r ( i=0; i<4; i++) 3 printf ( " tab[%d ] = %d\n", i, w [ i ] ) ; 4 5 zamien ( w, 4) ; 6 7 printf ( "\n" ) ; 8 printf ( "PO FUNKCJI: \ n\n" ) ; 9 f o r ( i=0; i<4; i++) 30 printf ( " tab[%d ] = %d\n", i, w [ i ] ) ; 4
31 3 free ( w ) ; 33 34 return 0 ; 35 } Gdy w funkcji za pomoc wska¹nika utworzymy dynamiczn tablic, mo»emy z funkcji zwróci wska¹nik do pierwszego jej elementu, aby (przykªadowo) w funkcji main mie dost p do tak utworzonej tablicy. Ponizszy kod pokazuje, jak napisa funkcj, która pobiera jako argument ilo± elementów tablicy, nast pnie za pomoc wska¹nika dynamicznie j tworzy (przydziela jej pami ) i zwraca wska¹nik do niej (wska¹nik na dynamiczn tablic / wska¹nik do pierwszego elementu dynamicznej tablicy). Nast pnie w funkcji main wska¹nik zwrócony z funkcji zapisujemy do innego wska¹nika i wykonujemy operacje na tablicy, ostatecznie zwalniaj c przydzielon pami : #i n c l u d e <s t d l i b. h> 3 #i n c l u d e <s t r i n g. h> 4 5 f l o a t * alokuj ( i n t n ) 6 { 7 f l o a t *f = ( f l o a t *) malloc ( s i z e o f ( f l o a t ) * n ) ; 8 return f ; 9 } 10 11 i n t main ( ) 1 { 13 i n t n ; 14 printf ( "Podaj i l o s c elementow t a b l i c y : \ n> " ) ; 15 scanf ( "%d", &n ) ; 16 17 f l o a t * wsk = alokuj ( n ) ; 18 19 i n t i=0; 0 f o r ( i=0; i<n ; i++) 1 { wsk [ i ] = i + 1 ; 3 printf ( "wsk[%d ] = %f \n", i, wsk [ i ] ) ; 4 } 5 6 free ( wsk ) ; 7 wsk = NULL ; 8 9 return 0 ; 30 } 5
9.11 Zadania do wykonania 9.11.1 Wska¹niki Zadanie 0.0 Zadeklaruj zmienn typu caªkowitego, a. Zadeklaruj wska¹nik na zmienn typu caªkowitego, wa. Niech wska¹nik wa wskazuje na zmienn a. Wy±wietl warto± zmiennej a korzystaj c ze zmiennej i ze wska¹nika. Zmie«warto± zmiennej - ponownie wy±wietl warto± zmiennej a korzystaj c ze zmiennej i ze wska¹nika. Do warto±ci wskazywanej przez wska¹nik dodaj warto± 100 (skorzystaj z operatora *). Ponownie wy±wietl warto± zmiennej a korzystaj c ze zmiennej i ze wska¹nika. Zadanie 0.1 Zadeklaruj zmienn typu caªkowitego, a. Zadeklaruj dwa wska¹niki na zmienn typu caªkowitego, wa1 oraz wa. Niech wska¹nik wa1 wskazuje na zmienn a, natomiast wska¹nik wa wskazuje na wska¹nik wa1. Wy±wietl warto± zmiennej a korzystaj c ze zmiennej i z wska¹ników. Zmie«warto± zmiennej - ponownie wy±wietl warto± zmiennej a korzystaj c ze zmiennej i z wska¹ników. Do warto±ci wskazywanej przez wska¹nik wa dodaj warto± 100 (skorzystaj z operatora *). Ponownie wy±wietl warto± zmiennej a korzystaj c ze zmiennej i z wska¹ników. Zadanie 0. Zadeklaruj zmienn typu zmiennoprzecinkowego zm1. Zadeklaruj wska¹nik na zmienn typu zmiennoprzecinkowego, wsk. Niech wska¹nik wsk wskazuje na zmienn zm1. Za pomoc funkcji scanf i przy u»yciu wska¹nika, wczytaj warto± do zmiennej zm1. Wy±wietl warto± zmiennej zm1 korzystaj c ze zmiennej i ze wska¹nika. Zadanie 0.3 Napisz funkcj, która pobiera wska¹nik do zmiennej typu int, a nast pnie wpisuje do zmiennej wskazywanej przez wska¹nik warto± 104. Do funkcji dopisz program, w którym zadeklraujesz zmienn zm1, oraz wska¹nik wsk. Do zmiennej wpisz warto± 56, wy±wietl warto± zmiennej zm1 korzystaj c ze zmiennej i ze wska¹nika. Nast pnie wywoªaj funkcj, przeka» jej wska¹nik wsk, i ponownie wy±wietl warto± zmiennej zm1 korzystaj c ze zmiennej i ze wska¹nika. Zadanie 0.4 Zadeklaruj zmienn typu caªkowitego zm1. Zadeklaruj wska¹nik na zmienn typu caªkowitego, wsk. Niech wska¹nik wskazuje na zmienn. Za pomoc funkcji scanf i przy u»yciu wska¹nika, wczytaj warto± do zmiennej zm1. Zwi ksz warto± zmiennej zm1 korzystaj c ze wska¹nika i operatora ++. Ponownie wy±wietl warto± zmiennej zm1 korzystaj c ze zmiennej i ze wska¹nika. Zwi ksz warto± zmiennej zm1 korzystaj c ze wska¹nika i operatora. Ponownie wy±wietl warto± zmiennej zm1 korzystaj c ze zmiennej i ze wska¹nika. Zadanie 0.5 Zadeklaruj i ustaw warto±ci (ró»ne) dwóm zmiennych typu zmiennoprzecinkowego. Zadeklaruj te» dwa wska¹niki. Pierwszy wska¹nik pow inien wskazywa na pierwsz ze zmiennych. Drugi wska¹nik ustaw tak, by wskazywaª n a pierwszy. Wy±wietl warto± obu wska¹ników oraz warto±ci, na które wskazuj. Nast p nie pod pierwsz ze zmiennych (typu zmiennoprzecinkowego) podstaw drug i ponownie wy±w ietl informacje o wska¹nikach. Na ko«cu pierwszej ze zmiennych wska¹nikowych przypisz warto± 5 i znów wy±wietl informacje o wska¹nikach. Zadanie 1 Napisz funkcj otrzymuj c jako argumenty wska¹niki do dwóch zmiennych typu int, która 6
zamienia ze sob warto±ci wskazywanych zmiennych. Zadanie Napisz funkcj otrzymuj c jako argumenty wska¹niki do dwóch zmiennych typu int, która zamienia ze sob warto±ci wskazywanych zmiennych tylko wtedy gdy wskazywana przez drugi argument zmienna jest mniejsza od zmiennej wskazywanej przez pierwszy argument. Zadanie 3 Napisz funkcj, której argumentami s dwa wska¹niki typu int za± zwracan warto±ci jest suma warto±ci zmiennych wskazywanych przez argumenty. Zadanie 4 Napisz funkcj, której argumentami s i typu int oraz w wska¹nik do int, która przepisuje warto± i do zmiennej wskazywanej przez w. Zadanie 5 Napisz funkcj otrzymuj c jako argumenty wska¹niki do dwóch zmiennych typu int, która zwraca jako warto± mniejsz z liczb wskazywanych przez argumenty. Zadanie 6 Napisz funkcj otrzymuj c jako argumenty wska¹niki do dwóch zmiennych typu int, która zwraca jako warto± wska¹nik na zmienn przechowuj c mniejsz z liczb wskazywanych przez argumenty. Zadanie 7 Napisz funkcj double abs_diff(double *a, double *b), która zwraca warto± bezwzgl dn ró»nicy warto±ci wskazywanych przez parametry wej±ciowe a i b. Zadanie 8 Napisz funkcj, która dostaje jako argumenty dwa wska¹niki na liczby zmiennoprzecinkowe i zwraca jako warto± ten z otrzymanych wska¹ników, który wskazuje na warto± o wi kszej warto±ci bezwzgl dnej. Zadanie 9 Napisz funkcj, która dostaje jako argument wska¹nik na liczb caªkowit i zapisuje do zmiennej wskazywanej przez argument warto± wczytan ze standardowego wej±cia. Zadanie 10 int avg(int a, int b, float *result); Zaimplementuj funkcje avg, tak aby zwracaªa 0, je»eli zmienna result wskazuje na NULL. W przeciwnym przypadku funkcja ma zapisa w zmiennej wskazywanej przez result ±redni arytmetyczn liczb a i b oraz zwróci 1. 9.11. Wska¹niki i tablice Zadanie 11 Napisz bezargumentow funkcj, która rezerwuje pami dla pojedynczej zmiennej typu int i zwraca jako warto± wska¹nik do niej. Zadanie 1 Napisz bezargumentow funkcj, która rezerwuje pami dla pojedyn- czej zmiennej typu double i zwraca jako warto± wska¹nik do niej. Zadanie 13 Napisz funkcj dostaj c w argumencie dodatni liczb caªkowit n i zwracaj c wska¹nik do pierwszego elementu n-elementowej dynamicznej tablicy int-ów. Zadanie 14 Napisz funkcj, która dostaje jako argument tablic int-ów i zwalnia pami zajmowan przez przekazan w argumencie tablic. Zadanie 15 Napisz funkcj, która dostaje jako argument wska¹nik do tablicy int-ów i odwraca kolejno± elementów w tablicy nie korzystaj c z dost pu do elementów tablicy operatorami [ ] oraz (). Zadanie 16 Napisz funkcj int previous(int *a, int *b), która dostaje jako argu- menty wska¹niki do dwóch ró»nych elementów tej samej tablicy i zwraca warto± tego o wcze±niejszym indeksie. Zadanie 17 Napisz funkcj, która jako argument otrzymuje wska¹nik do liczby caªkowitej n, alokuje pami na n-elementow tablic liczb zmiennoprzecinkowych, a nast pnie zwraca jako warto± adres do 7
tak przydzielonego bloku pami ci. Zadanie 18 Napisz funkcj, która jako argument otrzymuje wska¹nik do liczby caªkowitej n, alokuje pami na n-elementow tablic liczb zmiennoprzecinkowych, a nast pnie zwraca jako warto± adres do tak przydzielonego bloku pami ci. W funkcji main wczytaj od u»ytkownika rozmiar tablicy (n), wywoªaj funkcj przekazuj c jej podane n, a nast pnie wypeªnij utworzon tablic zerami i j wy±wietl, kolejno zwolnij przydzielon dla niej pami. 8