Laboratorium nr 6 Instrukcja laboratoryjna 6 1) 1/5 Język C Temat: Wskaźniki. Przygotował: mgr inż. Maciej Lasota 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]; Przykład: int wiek; char znak; //zwykle zmienne //zwykle zmienne int *wiek_wsk; char *znak_wsk; //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 Przykład: #include <stdio.h> #include <stdlib.h> int main(void) { int temp; int *wskaznik; temp = 100; printf("Liczba w zmiennej TEMP: %d\n",temp); wskaznik = &temp; // przypisujemy wskaznikowi // adres zmiennej TEMP *wskaznik = 200; // pod adres wskaznika // przypisujemy wartosc 200 printf("Liczba wskazywana przez wskaznik: %d\n",*wskaznik); return (0); } 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. Przykład: #include <stdio.h> #include <stdlib.h> int main() { 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); return (0); } 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. Przykład: #include <stdio.h> #include <stdlib.h> int main() { 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("\n"); 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]); printf("\n"); 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("\n"); printf("**macierz: \t%d\n",*macierz[0]); printf("*(*macierz+1): \t%d\n",*(*macierz+1)); printf("*(*macierz+2): \t%d\n",*(*macierz+2)); printf("\n"); 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)); return (0); } 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. Przykład: #include <stdio.h> #include <stdlib.h> void sum(int *k, int *l, int *s) { *s = (*k + *l); } int main() { 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); return (0); } 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 Przykład: 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);