Laboratorium nr 6 1/5 Język C Instrukcja laboratoryjna Temat: Wskaźniki. 6 Przygotował: mgr inż. Maciej Lasota 1) Wskaźniki. Wskaźniki (zmienne wskaźnikowe) stanowią jedno z fundamentalnych pojęć języka C. Wskaźnik jest to zmienna przechowująca adres (znajdujący się w pamięci operacyjnej komputera) innej zmiennej, tablicy lub funkcji (zadeklarowanej w programie). W języku C często wykorzystuje się wskaźniki, ze względu na to że jest to czasami jedyny sposób przedstawienia algorytmu obliczenia, częściowo zaś dlatego że ich użycie zwykle prowadzi do bardziej zwartego i efektywnego kodu. Deklaracja zmiennej wskaźnikowej polega na podaniu typu danych na jaki będzie wskaźnik wskazywał oraz podaniu gwiazdki * przed nazwą zmiennej. Często stosuje się ujednolicone nazewnictwo zmiennych wskaźnikowych (np. dopisując do każdej zmiennej _ptr, _wsk itp.) w celu wyeliminowania późniejszych pomyłek. Deklaracja zmiennej wskaźnikowej: [typ danych] *[nazwa zmiennej wskaźnikowej]; int wiek; char znak; int *wiek_wsk; char *znak_wsk; //zwykle zmienne //zwykle zmienne //zmienne wskaznikowe //zmienne wskaznikowe Do zarządzania wskaźnikami wykorzystujemy dwa specjalne operatory. Pierwszym operatorem jest operator gwiazdki *, drugim zaś operator ampersand &. * - operator dereferencji, czyli operator odwołania się danych wskazywanych przez dany wskaźnik. & - operator pobrania adresu (zmiennej, struktury, tablicy itp.).
Laboratorium nr 6 2/5 int main(void) int temp; int *wskaznik; temp = 100; printf("liczba w zmiennej TEMP: %d\n",temp); wskaznik = &temp; *wskaznik = 200; // przypisujemy wskaznikowi // adres zmiennej TEMP // pod adres wskaznika // przypisujemy wartosc 200 printf("liczba wskazywana przez wskaznik: %d\n",*wskaznik); 2) Wskaźniki a tablice jednowymiarowe. W języku C zacierają się różnice istniejące pomiędzy wskaźnikami i tablicami, w wielu przypadkach traktowane są tak samo. Wykonywana jest automatyczna konwersja między nimi. Elementy tablicy podczas tworzenia (uruchamiania programu) przypisywane są do kolejnych adresów w pamięci. Oznacza to że pierwszy element tablicy może znajdować się pod adresem 0xFF1000, natomiast kolejne elementy analogicznie pod adresami 0xFF1001, 0xFF1002,... itd. Taki sposób alokacji pamięci dla tablic w programach napisanych w języku C, pozwala na odnalezienie danego elementu tablicy za pomocą indeksów lub za pomocą wskaźników. int tab[4]=20,40,50,100; int *wsk;
Laboratorium nr 6 3/5 wsk = &tab[0]; printf("element pierwszy tablicy --> %d\n",*wsk); wsk = wsk + 1; printf("element drugi tablicy --> %d\n",*wsk); wsk++; printf("element trzeci tablicy --> %d\n",*wsk); wsk++; *wsk = 200; printf("element czwarty tablicy --> %d\n",*wsk); 3) Wskaźniki a tablice wielowymiarowe. W języku C tablice wielowymiarowe, reprezentowane są jako tablice tablic (każdy element tablicy zawiera wskaźnik na tablicę). Zwiększenie liczby wymiarów tablicy zwiększa równocześnie poziom skomplikowania z punktu widzenia wskaźników. int macierz[4][4] = 3,2,1,0, 5,9,0,3, 1,2,8,4, 2,5,6,7 ; printf("macierz: \t%p\t\tmacierz+1: \t%p\n",macierz,macierz+1); printf("macierz[0]: \t%p\t\tmacierz[0]+1: \t%p\n",macierz[0],macierz[0]+1); printf("*macierz: \t%p\t\t*macierz+1: \t%p \n",*macierz,*macierz+1); printf("macierz[0][0]: \t%d\n",macierz[0][0]); printf("macierz[1][1]: \t%d\n",macierz[1][1]); printf("macierz[2][3]: \t%d\n",macierz[2][3]);
Laboratorium nr 6 4/5 printf("*macierz[0]: \t%d\n",*macierz[0]); printf("*macierz[1]: \t%d\n",*macierz[1]); printf("*macierz[2]: \t%d\n",*macierz[2]); printf("**macierz: \t%d\n",*macierz[0]); printf("*(*macierz+1): \t%d\n",*(*macierz+1)); printf("*(*macierz+2): \t%d\n",*(*macierz+2)); printf("**macierz: \t\t%d\n",*macierz[0]); printf("*(*(macierz+1)+1): \t%d\n",*(*(macierz+1)+1)); printf("*(*(macierz+2)+3): \t%d\n",*(*(macierz+2)+3)); 4) Wskaźniki a funkcje. W języku C parametry do funkcji przekazywane są zawsze przez wartość (z wyjątkiem przekazywania tablic). Oznacza to, że w ciele funkcji operujemy jedynie na kopiach zmiennych. Czasami jednak w programie przydatna jest możliwość modyfikacji przekazywanych zmiennych. Przekazując do funkcji zamiast zwykłych zmiennych wskaźniki do tych zmiennych, mamy możliwość modyfikacji oryginalnych wartości przechowywanych przez zmienne w ciele funkcji. void sum(int *k, int *l, int *s) *s = (*k + *l); int a,b; int suma; printf("podaj liczbe a: "); scanf("%d",&a); printf("podaj liczbe b: "); scanf("%d",&b);
Laboratorium nr 6 5/5 sum(&a,&b,&suma); printf("suma: (%d + %d) = %d\n",a,b,suma); 5) Wskaźniki a struktury danych. W przypadku, gdy mamy zadeklarowany wskaźnik na strukturę danych, dostęp do poszczególnych składowych struktury odbywa się za pomocą tzw. notacji strzałkowej. Notacja ta polega na podaniu wskaźnika do struktury, -> (strzałki) oraz składowej do, której się odwołujemy struct data int dzien; int miesiac; int rok; data_urodzenia; struct data *wsk; wsk = &data_urodzenia; scanf( %d,&wsk->dzien); scanf( %d,&wsk->miesiac); scanf( %d,&wsk->rok); printf( Data urodzenia: %d-%d-%d\n,wsk->dzien,wsk->miesiac,wsk->rok);