Informatyka I Wykład 10 WSKAŹNIKI I ADRESY Jerzy F. Kotowski
WSKAŹNIKI I ADRESY - definicje Składnia linii deklaracji wskaźnika type_name *identifier; Dla większości typów T, T* jest wskażnikiem do T. Oznacza to, że zmienna typu T* może przechowywać adres obiektu typu T. Przykład: float *p_f; Sprawa się komplikuje jeżeli T jest obiektem typu tablica lub funkcja. Kiedyś o tym będzie. Należy przyjść wyspanym!!
Nowe operatory jednoargumentowe Operator wskazania i wyłuskania Operator wskazania & ampersand Zwraca adres swojego argumentu Składnia: & l_value Przykład: int x, *p_i; p_i = &x; Można tak: int x, *p_i = &x; Operator wyłuskania * Odwołanie do obiektu poprzez wskaźnik (dereferencja) Składnia: * adres Przykład: int x = 5, y, *p_i = &x; y = *p_i; *p_i = 6; To są całkiem inne gwiazdki!!
ADRESY i TABLICE double Ula[100]; double *p_d; p_d = &Ula[0]; Nazwa tablicy jest adresem do jej zerowego elementu p_d = Ula; Co to jest?: *Ula Dereferencja do obiektu pod adresem Ula: Ula[0] Uwaga!! Nie można napisać Ula = p_d; Ula nie jest wskaźnikiem tylko adresem. Analogie: x = 2; OK! 2 = x; ERROR! Nazwa tablicy jest stałym adresem
Arytmetyka adresów i wskaźników Operator podstawiania Kilka razy to już było: wskaźnik = adres; Niejawna konwersja adresów: char x; int *p_i; p_i = &x; ERROR!!! p_i = (int *) &x; OK! Adresy wymagają jawnej konwersji Wskaźnik typu void Składnia void *ident; char *p_c; int *p_i = (int *) p_c; Alternatywa: char *p_c; int *p_i; void *p; p = p_c; p_i = p; Dereferencja nie ma sensu: *p
Arytmetyka adresów i wskaźników Operatory inkrementacji i dekrementacji Składnia: ++ l_value tzn. argumentem musi być wskaźnik a nie adres Przykład: double Ala,*p = &Ala; p++; Zmiana wartości wskaźnika o jeden obiekt long Ela[4] = {2,4,5,8}, *p = Ela, *q = &Ela[3]; p++; *p: 4 q--; *q: 5
Arytmetyka adresów i wskaźników Operatory logiczne W kontekście z adresami można używać operatorów logicznych Operatory przyrównania == != Operatory relacji < <= > >= Przykład long s, A[10], *p=A; long *q=&A[9], *r=&A[7]; .………… s=0; while(p<=q) if(p!=r) s+=*p++;
Arytmetyka adresów i wskaźników Komentarz Dwa operatory jednoargumentowe: *p++ Zwiększamy wskaźnik!! to znaczy: *p++ ÜÞ *(p++ ) Tego się nie wymyśli łatwo samemu w oparciu o reguły łączności i priorytety! (*p)++ - Zwiększenie wartości pod wskazaniem A to jest proste: *--p --*p ponieważ wszystkie operatory jednoargumentowe są prawostronnie łączne inf_10_1
Arytmetyka adresów i wskaźników Przykład Kopiownie łańcucha wraz ze znakiem końca ‘\0’ char A[5]=“Ala”; char B[5]; char *p=A, *q=B; while(*q++=*p++); Wpierw skopiujemy znak ‘\0’ a potem przetestujemy 2 pary nawiasów Funkcja kopiuj (strcpy) void kopiuj(char *d,char *s) { while(*d++=*s++); } s - source d - destination Przykład wywołania kopiuj(B,A) inf_10_1
Arytmetyka adresów i wskaźników Dodawanie adresu do liczby całkowitej Składnia: adres + liczba Wynikiem jest adres przesunięty względem argumentu o ‘liczba’ obiektów Działanie przemienne liczba może być ujemna int Ala[10]; int *p = A+5, *q = p-2; p ® A[5], q ® A[3] Najważniejszy wzór języka C a[i] º *(a+i) Te zapisy są zawsze równoważne Jest to definicja [ ], tzn. operatora indeksowego Prosta konsekwencja: a[i] º *(a+i) º *(i+a) º i[a] inf_10_1
Wariacje na temat iloczynu skalarnego dwóch wektorów int A[N], B[N],i,i_p; coś podstawiamy do A i B i_p = 0; Wariacja I - klasyka for(i=0;i<N;i++) i_p += A[i]*B[i]; Wariacja II - wskaźniki int *p=A, *p=B; i=N; while(i--) i_p += *p++**q++; Uwaga! Tak nie wolno! i_p += *A++**B++; Wariacja III - funkcja int in_pr(int *p, int *q, int n) { int i, i_p =0; // i zbędne while(n--) // zm. kopię i_p += *p++**q++; return i_p; } Wariacja IV - pełny odlot for(i=0;i<n;i++) i_p += p[i]*q[i]; Wywołanie i_p = in_pr(A,B,10); inf_10_2
Arytmetyka adresów i wskaźników Odejmowanie dwóch adresów Składnia: adr1 - adr2 Wynikiem jest liczba określająca odległość między adresami mierzoną w obiektach Adresy muszą być tego samego typu Najlepiej jak wskazują na elementy tej samej tablicy Nie ma więcej operatorów. Problem: jak trafić w środek pomiędzy dwa adresy p i q. Nie można napisać r = (p+q)/2; Rozwiązanie r = p + (q-p)/2;
KONIEC! OSTATNI Przykład Funkcja zwracająca długość łańcucha int str_len(char *p) { char *q=p; while(*q) q++; return q - p; // ! } Odejmowanie return q - p; // ! Przykłady wywołań char Ala[10]=“Ala”; char *p = “Ala i Ola”; int d; d = str_len(Ala); d = str_len(p); d = str_len(“Ala ma kota”); KONIEC! inf_10_3