1 / 28

Wyk ł ad 1 : Wskaźniki

Podstawy programowania Programowanie w C. Wyk ł ad 1 : Wskaźniki. Dr inż. Zdzisława Rowińska III p. KIS, pokój 314, zrow@kis.p.lodz.pl. Gdzie wykorzystywane są wskaźniki?. Praca z tablicami Funkcje – zmiana wartości przesyłanych argumentów Dostęp do specjalnych komórek pamięci

deva
Télécharger la présentation

Wyk ł ad 1 : Wskaźniki

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Podstawy programowania Programowanie w C Wykład 1: Wskaźniki Dr inż. Zdzisława Rowińska III p. KIS, pokój 314, zrow@kis.p.lodz.pl

  2. Gdzie wykorzystywane są wskaźniki? • Praca z tablicami • Funkcje – zmiana wartości przesyłanych argumentów • Dostęp do specjalnych komórek pamięci • Rezerwacja obszarów pamięci

  3. Definiowanie wskaźników Definiowanie wskaźników: typ_obiektu *nazwa_wskaźnika; nazwa_wskaźnikawskazuje na obiektu typu typ_obiektu. Przykłady Można definiować wskaźniki do obiektów różnych typów: int *wi; char *wch; float *wf; • Uwaga: • Z definicji wskaźnika wynika, że wskaźnik pokazuje na obiekt. Referencja nie jest obiektem, dlatego nie można definiować wskaźników do referencji. • Wskaźnik, który pokazuje na obiekt jednego typu, nie może być wykorzystany do pokazywania na obiekt innego typu.

  4. Wskaźniki Sama definicja wskaźnika nie powoduje, że wskaźnik wskazuje na konkretnyobiekt. Nadanie wartości obiektowi (wskaźnik wskazuje na istniejący obiekt): int *wi; int i; wi=&i; //ustawienie wskaźnika w na obiekt i Jeśli wskaźnik wskazuje na konkretny obiekt, to można odnosić się do tego obiektuza pomocą wskaźnika. Do operacji tej służy jednoargumentowy operatorodniesienia (wyłuskania, dereferencji) * *nazwa_wskaźnika; Przykład: int *wi; //definicja wskaźnika int i; //definicja zmiennej i wi=&i; //ustawienie wskaźnika *wi=5; //przypisanie wartości 5 zmiennej i

  5. Wskaźniki – cd Przykłady: int *wi,*wj; //definicja wskaźnika int i,j; //definicja zmiennych i,j wi=&i; i=5; *wi=5; j=7; j=*wi; wi=&j; wj=wi;

  6. Wskaźniki typu void void *wv; Definicja wskaźnika bez podania typu obiektu, na jaki wskazuje. Może być użytydo wskazywania na obiekty dowolnego typu. Przykłady: void *wv; int *wi; float *wf; wv=wi; //teraz wskaźnik wv wskazuje na ten sam obiekt (typu int), //na który wskazuje wskaźnik wi wv=wf; wi=wf //kompilator zasygnalizuje błąd!! wi=(int *)wf; //wykorzystanie rzutowania wf=(float *)wv; //wykorzystanie rzutowania Wskaźnikowi typu void można przypisać wskaźnik dowolnego (niestałego) typu.Działanie odwrotne wymaga operatora rzutowania.

  7. int *wsk; //definicja wskaźnika int tab[10]; //definicja tablicy wsk=&tab[indeks]; //ustawienie wskaźnika na elemencie tablicy //o indeksie indeks wsk=&tab[0]; wsk=tab; wsk=&tab[indeks]; wsk=wsk+ind; wsk +=ind; instrukcje równoważne //przesunięcie wskaźnika o ind pozycji Zastosowanie wskaźników do tablic Dodanie do wskaźnika liczby całkowitej ind powoduje, że wskaźnik pokazujeo indelementów dalej w tablicy; niezależnie od tego, jakiego typu są elementytablicy.

  8. Zastosowanie wskaźników do tablic Przykład: #include <iostream.h> void main(){ float *wf; float tab[10]; wf=tab; //lub wf=&tab[0]; for(int i=0; i<10; i++){ *(wf++)=i/10.0; } for(i=0,wf=tab;i<10;i++,wf++) printf(”%f\n”,*wf); }

  9. Nazwa tablicy i wskaźnik Nazwa tablicy jest jednocześnie adresem jej zerowego elementu. Nazwa tablicy jest stałym wskaźnikiem do jej zerowego elementu. float *wsk; float tab[10]; wsk=tab; //wsk=&tab[0]; //możliwe jest przypisanie wsk++; //niemożliwa jest instrukcja tab++; tab[ind]; *(tab+ind); Wskaźnik jest pewnym obiektem w pamięci (to znaczy posiada swój adres). Nazwa (również tablicy) nie jest obiektem (nie ma więc adresu). float *wsk; &wsk; //adres wskaźnika – odwołanie poprawne

  10. Wskaźniki w argumentach funkcji void fun(float f) { f /= 3.3; } float fun(float *f) { *f /= 3.3; } Przesyłanie argumentów przez wartość. Funkcja nie zmienia wartości przesyłanego argumentu. void main() { float fvar = 13.3; fun(fvar); printf(”%f\n”, fvar); // 13.3 fun(&fvar); printf(”%f\n”, fvar);// 4.03 } Przesyłanie argumentówprzez wskaźnik. Zmiana wewnątrz funkcjiwartości przesyłanegoargumentu.

  11. Przesyłanie tablicdo funkcji #include <iostream.h> void f1(int *pi, int rozm); void f2(int *pi, int rozm); void f3(int t[], int rozm); void main(){ int tab[5]={1,2,3,4,5}; f1(tab,5); f2(tab,5); f3(tab,5); } void f1(int *pi, int rozm){ printf("\nfunkcja f1\t”); for(int i=0;i<rozm;i++) printf(”%f\t”,*(pi++)); } void f2(int *pi, int rozm){ printf("\nfunkcja f1\t”); for(int i=0;i<rozm;i++) print(”%f\t”,pi[i]); } void f3(int t[], int rozm){ printf("\nfunkcja f3\t”); for(int i=0;i<rozm;i++) print(”%f\t”,t[i])); } Funkcje są wywoływane przez podanienazwy tablicy Przesłany adres tablicy inicjalizujelokalny wskaźnik pi. Wewnątrz funkcji- zapis wskaźnikowy Przesłany adres tablicy inicjalizujelokalny wskaźnik pi. Wewnątrz funkcji - zapis tablicowy Przesłany adres tablicy odebranyjako tablica

  12. Przesyłanie tablic do funkcji – cd • Odebranie tablicy jako tablicy – czytelność funkcji • Odebranie tablicy jako adresu inicjującego wskaźnik – funkcja działa szybciej • Odebranie tablicy jako adresu – łatwiejsze przekazywanie tablic wielowymiarowych (rozmiary tablicy nie muszą być znane w momencie wywołania funkcji)

  13. Wskaźniki do stałych Wskaźniki do stałych mogą zawierać adresy dowolnych zmiennych i mogą być modyfikowane w programie, ale nie można za ich pomocą modyfikować zmiennych wskazywanych. const int stala = 10; // definicja stałej typu int const int *wsk_st; // wskaźnik do stałej typu int void main() { int i = 5; const int *w = &i; // wskaźnik zainicjowany wsk_st = &i; // inicjacja adresem zmiennej automatycznej i cout << *wsk_st << endl; // 5 cout << *w << endl; // 5 // *w = *w + 5; // błąd kompilatora ! // *wsk_st+= 7; // nie można modyfikować stałej wsk_st = &stala; // wskaźnik inicjowany adresem stałej w = &stala; // wskaźnik inicjowany adresem stałej cout << *wsk_st << endl; // 10 cout << *w << endl; // 10 }

  14. Stałe wskaźniki Stały wskaźnik to wskaźnik, który zawsze pokazuje na to samo. Wskaźnik tego typu musi być zainicjowany w miejscu definicji - tak jak każda stała. W programie nie można już modyfikować jego wartości. Za pomocą wskaźnika stałego można jednak modyfikować zawartość zmiennej wskazywanej, ale tylko tej, której adresem wskaźnik został zainicjowany. const int st = 4; // stała int zm = 10; // definicja zmiennej int * const w = &zm; // stały wskaźnik do zmiennej zm // int * const x = &st; // błąd – wskaźnik musi wskazywać na zmienną const int * const x = &st; // dobrze - stały wskaźnik do stałej int i = 7; int * const wi = &i; // stały wskaźnik do zmiennej lokalnej i int * const pz = &zm; // stały wskaźnik do zmiennej zm cout << *wi << endl; // i=7 = i cout << *pz << *w << endl; // 10 i 10; zm=10 *wi +=8; // i = 7 + 8 = 15 cout << i << endl; // i =15 // wi = &zm; // błąd – nie wolno zmieniać stałej wi *pz += 2; // zm = 10 + 2 cout << zm << endl; // zm = 12

  15. Wskaźniki do funkcji typ_zwracany(*wsk_do_funkcji)([argumenty_funkcji]); #include <iostream.h> void f1(); void f2(); void blad(); void main(){ void (*pf)()=blad; int i; cin>>i; if(i==1) pf=f1; else if(i==2) pf=f2; (*pf)(); } void f1(){ cout<<"funkcja f1\n"; } void f2(){ cout<<"funkcja f2\n"; } void blad(){ cout<<"blad\n"; }

  16. Wskaźniki do funkcji • Nazwa funkcji jest adresem jej początku (adresem miejsca w pamięci, gdziezaczyna się kod tej funkcji). • Zastosowanie wskaźników do funkcji: • Przesyłanie argumentów do funkcji. Adres funkcji można wysłać jako argument. • Tworzenie tablic ze wskaźników do funkcji.

  17. #include <iostream.h> void f1(); void f2(); void blad(); void funkcja(int i, void (*pf)()); void main(){ void (*pf)()=NULL; int i; cin>>i; funkcja(i,pf); } void funkcja(int i, void (*pf)()){ pf=blad; if(i==1) pf=f1; else if(i==2) pf=f2; (*pf)(); } void f1(){ cout<<"funkcja f1\n"; } void f2(){ cout<<"funkcja f2\n"; } void blad(){ cout<<"blad\n"; } Przesyłanie argumentów do funkcji

  18. Tablice wskaźników typ_wskazywany*tablica[rozmiar]; //typ_wskazywany*(tablica[rozmiar]); zapis równoważny double *tablica[10];

  19. Tablice wskaźników - cd #include <iostream.h> void f1(); void f2(); void blad(); void main(){ void (*pt[3])()={f1,f2,blad}; for(int i=0;i<3;i++) (*pt[i])(); } void f1(){ cout<<"funkcja f1\n"; } void f2(){ cout<<"funkcja f2\n"; } void blad(){ cout<<"blad\n"; } Tablica wskaźników do funkcji

  20. Pamięć a zmienne w programie Ze względu na czas życia wyróżnia się: - obiekty statyczne - istniejące od chwili rozpoczęcia działania programu aż do jego zakończenia - obiekty dynamiczne - tworzone i usuwane z pamięci w trakcie wykonania programu: - automatycznie, czyli bez udziału programisty - kontrolowane przez programistę

  21. Pamięć a zmienne w programie O tym czy obiekt jest statyczny czy dynamiczny decyduje miejsce deklaracji oraz klasa pamięci (przypisywana jawnie w deklaracji lub przyjmowana domyślnie). Klasę pamięci określają słowa kluczowe: auto, static, register, umieszczane przed deklaracją zmiennej, np. static int x; • wszystkie zmienne globalne są statyczne • wszystkie zmienne lokalne zadeklarowane bez jawnego specyfikowania klasy pamięci są automatyczne (auto). Zmienną lokalną można uczynić statyczną dodając przed deklaracją static (czasami stosowane w funkcjach) • zmienne klasy register są automatyczne

  22. Funkcje malloc, calloc, free Dynamiczna alokacja pamięci W języku C/C++ istnieją standardowe funkcje umożliwiające dynamiczną alokację pamięci (malloc i calloc) oraz funkcja zwalniająca przydzieloną pamięć (free). Prototypy funkcji: void *malloc(size_t K); // alokacja K bajtów void *calloc(size_t N, size_t K); // alokacja N razy po K bajtów void free(void *x); // zwolnienie obszaru • Funkcje malloc i calloc przydzielają spójne obszary pamięci, których rozmiary nie przekraczają 64 KB (size_t jest zdefiniowany jako unsigned) i zwracają wskazanie do przydzielonego obszaru. • Jeżeli alokacja nie jest możliwa, to zwracany jest wskaźnik NULL. Funkcja calloc dodatkowo zeruje przydzieloną pamięć. Funkcja free zwraca do systemu przydzieloną pamięć. • Funkcje malloc i calloc zwracają wskaźniki do typu void dlatego niezbędne są konwersje typu przy podstawieniach do wskaźników innych typów. • Należy uważać, aby za pomocą funkcji free nie zwalniać pamięci, która nie została przydzielona.

  23. Funkcje malloc, calloc, free Dynamiczna alokacja pamięci Przykład: void main() { int i; char* ps = NULL; //wskaźnik do char ps = (char*) malloc(5 * sizeof(char)); //alokacja pamięci na //elementy typu char if (ps) { strcpy(ps, "Ola"); for (i = 0; i < strlen(ps); i++) printf("%c\n", *(ps + i)); //wypisanie i-tego //elementu tablicy free(ps); //zwolnienie pamięci ps = NULL; } }

  24. Dynamiczny przydział pamięci - przykładPrzydział pamięci dla n-liczb typu int, wczytanie liczb, obliczenie średniej #include <stdio.h> #include <stdlib.h> int main() { int *tab, i, n, x; float suma = 0.0; printf("Podaj ilosc liczb: "); scanf("%d",&n); tab = (int *) calloc(n,sizeof(int)); if (tab == NULL) { printf("Nie mozna przydzielic pamieci.\n"); exit(-1); } for (i=0; i<n; i++) /* wczytanie liczb */ { printf("Podaj liczbe nr %d: ",i+1); scanf("%d",&x); tab[i] = x; } for (i=0; i<n; i++) suma = suma + tab[i]; printf("Srednia %d liczb wynosi %f\n",n,suma/n); free(tab); return 0; }

  25. Dynamiczny przydział pamięci na tablicę dwuwymiarową (macierz): Zamiast standardowego odwołania do elementów macierzy: tab[i][j] stosujemy odwołania do odpowiednich elementów wektora: *(tab+M*i+j) lub tab[M*i+j], gdzie M –liczba kolumn Przykład: tablica tab, dla której N=3, M=4 odwołujemy się do elementu tab[2][2]: *(tab+4*2+2)

  26. Przydział pamięci na tablicę NxM, wygenerowanie i wyświetlenie liczb #include <stdio.h> #include <stdlib.h> #include <time.h> #define N 4 #define M 6 int main() { int i,j,*tab; tab = (int *) calloc(N*M,sizeof(int)); srand(time(NULL)); for (i=0;i<N;i++) for (j=0;j<M;j++) *(tab+M*i+j) = rand()%100; for (i=0;i<N;i++) { for (j=0;j<M;j++) printf("%4d",tab[M*i+j]); printf("\n"); } free(tab); return 0; }

  27. Metoda 2 (wskaźnik na tablicę wskaźników): Przydzielamy pamięć na N-elementowy wektor wskaźników na typ int, a następnie do kolejnych elementów tego wektora zapisujemy adresy M-elementowych wektorów liczb typu int (pamięć na wektory jest także przydzielana dynamicznie) int **tab; tab = (int**) calloc(N,sizeof(int *)); for (i=0; i<N; i++) tab[i] = (int*) calloc(M,sizeof(int));

  28. Przydział pamięci na tablicę NxM, wygenerowanie i wyświetlenie liczb #include <stdio.h> #include <stdlib.h> #include <time.h> #define N 4 #define M 6 int main() { int i, j, **tab; tab = (int **) calloc(N,sizeof(int *)); for (i=0;i<N;i++) tab[i] = (int*) calloc(M,sizeof(int)); srand(time(NULL)); for (i=0;i<N;i++) for (j=0;j<M;j++) tab[i][j] = rand()%100; for (i=0;i<N;i++) { for (j=0;j<M;j++) printf("%4d",tab[i][j]); printf("\n"); } for (i=0;i<N;i++) free(tab[i]); /*w pierwszej kolejności zwalniamy pamięć przydzieloną na N wektorów, każdy o rozmiarze M */ free(tab); /*zwalniamy pamięć przydzieloną na N-elementowy wektor wskaźnikow na typ int */ return 0; }

More Related