Podstawy programowania Zachodniopomorska Szkoła Biznesu Podstawy programowania Wykład 5: Tablice i wskaźniki
Podstawy programowania - Tablice i wskaźniki Dane złożone Często do opisu pewnego obiektu lub grupy obiektów nie wystarcza pojedyncza zmienna konieczne jest wtedy zdefiniowanie zbioru różnych zmiennych, które tworzą pewną całość W języku C++ dla opisu takiej sytuacji stosuje się typy złożone, które pozwalają pod jedną nazwą zmiennej pozwalają ukryć wiele prostych wartości Najważniejsze typy złożone: tablica struktura (rekord) klasa (programowanie obiektowe) Podstawy programowania - Tablice i wskaźniki
Podstawy programowania - Tablice i wskaźniki Stosowane są gdy chcemy w sposób uporządkowany zapisać zbiór wartości tego samego typu, np. oceny poszczególnych studentów z grupy ceny produktów w ofercie nazwiska znajomych Każdy element tablicy ma swój indeks (numer), który jednoznacznie go identyfikuje Można powiedzieć, że tablica to uporządkowany zbiór zmiennych jednakowego typu Zdefiniowanie tablicy wymaga podania typu elementu oraz liczby elementów Ze względu na sposób uporządkowania elementów można wyróżnić tablice jednowymiarowe, dwuwymiarowe, itd. Rozmiar tablicy (liczba elementów) musi być określona już w trakcie pisania programu Podstawy programowania - Tablice i wskaźniki
Deklaracja tablicy jednowymiarowej Składnia: typ nazwa[liczba_elementów]; // całkowita>0 Przykłady float oceny_grupy[30]; int duzy_lotek[6]; int a,b[10],c,d[2]; // b,d - tablice Inicjalizacja tablicy: float punkt3D[3] = {0, 2, 1.5 }; int duzy_lotek[3+3]= {2,4,6,7}; // można mniej Nie można podać więcej wartości niż zdefiniowano w tablicy. b[0] b[1] b[2] b[3] b[4] b[5] b[6] b[7] b[8] b[9] tablica b: Podstawy programowania - Tablice i wskaźniki
Podstawy programowania - Tablice i wskaźniki Korzystanie z tablicy Dostęp do tablicy odbywa się dla każdego elementu osobno, każdy z nich można wykorzystywać jak normalną zmienną, należy w tym celu podać nazwę tablicy oraz wyrażenie określające indeks pomiędzy znakami: ‘[‘ i ‘]‘ Indeksy elementów w C++ zaczynają się zawsze od 0 !!! Jeżeli wielkość tablicy to N, indeksy elementów to 0 ... N-1 Nie wolno korzystać z elementów o indeksach wykraczających poza zadeklarowany zakres Przykłady: int x,tablica[10]; tablica[0]=1; tablica[5]=2; tablica[6]=tablica[5]+2; x=2*tablica[6]; tablica[x+1]=10; // obliczenie indeksu tablica[10]=0; // błąd: poza tablicą!!! jakie wartości są teraz w tablicy? Podstawy programowania - Tablice i wskaźniki
Algorytmiczny dostęp do tablicy Wykorzystanie zmiennych do określania indeksu umożliwia dostęp do poszczególnych elementów za pomocą pętli, zwykle jest to pętla for Przykładowy fragment programu: float tablica[10]; for (int i=0; i<5; i++) // zerowanie elementów 0-4 tablica[i]=0; for (i=5; i<10; i++) tablica[i]=i-1; // el. 5-9, wartości 4-8 tablica po wykonaniu programu: indeks 1 2 3 4 5 6 7 8 9 wartość Podstawy programowania - Tablice i wskaźniki
Sumowanie elementów tablicy #include <iostream.h> #define N 10 void main() { int i, tab[N]; // N to nie zmienna! for (i=0; i<N; i++) // pobieranie danych { cout << "Podaj element " << i << ":"; cin >> tab[i]; } int suma=0; // obowiązkowe wyzerowanie for (i=0; i<N; i++) suma+= tab[i]; // czyli suma=suma+tab[i] cout << ”Średnia wartość:” << (float)suma/N << endl; } Podstawy programowania - Tablice i wskaźniki
Wartość maksymalna / minimalna float tab[10]; ... // pomijamy wprowadzanie float min=tab[0], max=tab[0]; int nrmin=0, nrmax=0; for (int i=0; i<N; i++) // szukamy największego if (tab[i]>max) { max=tab[i]; nrmax=i; } for (i=0; i<N; i++) // szukamy najmniejszego if (tab[i]<min) { min=tab[i]; nrmin=i; } cout << ”Największy jest element: ” << nrmax; cout << ", wynosi: " << max << endl; cout << ”Najmniejszy jest element: ” << nrmin; cout << ", wynosi: " << min << endl; Podstawy programowania - Tablice i wskaźniki
Szukanie i zliczanie elementów spełniających podane kryterium float tab[10]; ... // pomijamy wprowadzanie int licznik=0; // wyzerowanie licznika for (int i=0; i<N; i++) { cout << "element " << i << ": "; if (tab[i]>0) // szukamy liczb dodatnich { cout << tab[i] << " jest dodatni\n"; licznik++; } } cout << "Dodatnie : " << licznik << endl; cout << "Niedodatnie: " << N-licznik << endl; Podstawy programowania - Tablice i wskaźniki
Tablica jako parametr funkcji Jeżeli funkcja przyjmuje tablicę jako parametr, wyjątkowo nie jest ona przesyłana jako kopia. Przesyłany jest adres tablicy źródłowej. Funkcja może więc zmienić zawartość tej tablicy W nagłówku funkcji można pominąć rozmiar tablicy ponieważ nie jest to deklaracja nowej tablicy (pamięć już przydzielono) Przykład: void zeruj(float tab[], int n) { for (int i=0; i<n; i++) tab[i]=0.0; } void main() { float tablica[10]; zeruj(tablica, 10); } Podstawy programowania - Tablice i wskaźniki
Deklaracja tablicy dwuwymiarowej Można myśleć o takiej tablicy jak o macierzy prostokątnej Zawiera więc ona NxM elementów, które uporządkowane są w N kolumn i M wierszy W składni języka C++ należy ją rozumieć jako tablicę tablic Składnia: typ nazwa[M][N]; // tablica M tablic N-element. Przykład: float oceny[30][5]; // 30 studentów po 5 ocen Dostęp do elementu (wiersz i, kolumna j): nazwa[i][j]; np. oceny [1][3]; Każdy z indeksów numerowany jest od 0! 5 30 ... Podstawy programowania - Tablice i wskaźniki
Korzystanie z tablicy dwuwymiarowej W celu przetworzenia wszystkich elementów tablicy z reguły używa się dwóch zagnieżdżonych pętli for (pętla w pętli) Przykład: utworzenie i wydruk macierzy jednostkowej 10x10: float macierz [10][10]; for (int i=0; i<10; i++) // kolejne wiersze for (int j=0; j<10; j++) // kolejne kolumny if (i==j) macierz[i][j]=1; else macierz[i][j]=0; // krócej: macierz[i][j]= (i==j); for (i=0; i<10; i++) { for (j=0; j<10; j++) cout << macierz[i][j] << " "; cout << endl; } Podstawy programowania - Tablice i wskaźniki
Tablica dwuwymiarowa jako parametr funkcji void zerujmacierz(float tab[][5], n) // n wierszy for (int i=0; i<n; i++) // m musimy znać for (int j=0; j<5; j++) tab[i][j]=0.0; } void zerujwiersz(float tab[], m) // m - dł. wiersza { for (int i=0; i<n; i++) tab[i]=0.0; } void main() { float tablica[10][5]; zerujmacierz(tablica,30); zerujwiersz(tablica[2], 5); // zerujemy wiersz 2 } Podstawy programowania - Tablice i wskaźniki
Podstawy programowania - Tablice i wskaźniki W wielu sytuacjach istnieje potrzeba przechowywania lub przesyłania informacji na temat położenia zmiennej w pamięci. W języku C/C++ służą do tego zmienne o specjalnych typach danych zwanych wskaźnikami (ang. pointer) Zawierają one informacje o: adresie danej w pamięci - typie danej (a więc i o jej rozmiarze w bajtach) Każdemu z typów podstawowych można przypisać typ wskaźnikowy przeznaczony do pokazywania na zmienne tego typu. Typ wskaźnikowy określany jest za pomocą operatora *, jako typ * , np. wskaźnik do zmiennej typu int to int * Pobranie adresu zmiennej odbywa się za pomocą operatora &, a pobranie wartości z adresu, na który pokazuje wskaźnik - za pomocą operatora * stojącego po jego lewej stronie Uwaga! Wskaźnik zawsze na coś pokazuje! Podstawy programowania - Tablice i wskaźniki
Podstawy programowania - Tablice i wskaźniki Wskaźniki - przykład void main() { int a=10,b=20; int *iwsk; // wskaźnik na int iwsk=&a; // iwsk pokazuje teraz na a b= *iwsk+1; // pobranie wartości spod adresu iwsk=&b; // iwsk pokazuje teraz na b float x, *fwsk; // fwsk jest typu float* iwsk= &x; // błąd - inny typ wskaźnika!!! fwsk=iwsk; // j.w. fwsk=&x; x=5; cout << *fwsk<< endl; // wypisze 5 x=6; cout << *fwsk<< endl; // wypisze 6 ! } Podstawy programowania - Tablice i wskaźniki
Operacje na wskaźnikach Porównywanie (operatory ==, !=, <, >, <=, >=) porównywane są adresy zapisane we wskaźnikach, ale typy bazowe muszą być jednakowe Odejmowanie wskaźników można odjąć dwa wskaźniki o jednakowych typach bazowych. Wynikiem będzie różnica adresów podzielona przez rozmiar typu bazowego (ile elementów dzieli te adresy) Dodawanie/odejmowanie od wskaźnika wartości całkowitej oznacza przesunięcie zapisanego adresu o podaną liczbę elementów. Liczba bajtów przesunięcia zależy od rozmiaru typu bazowego. Można korzystać również z operatorów ++ i -- Wskaźnik zerowy (NULL) wartość 0 jako jedyna traktowana jest jako nie wskazująca na nic. Symbol NULL zdefiniowany jest w stdio.h, stdlib.h, i w iostream.h Podstawy programowania - Tablice i wskaźniki
Przykłady operacji na wskaźnikach int *wsk1, *wsk2=NULL; *wsk1=5; // uwaga! wskaźnik przypadkowy!!! *wsk2=5; // tutaj zareaguje system int a,b,c; wsk1=&a; wsk2=&c; cout << "Adres a: " << wsk1 << end; // można wypisywać cout << "Następny po a: " << wsk1+1 << endl; cout << "Różnica c i a to: " << wsk2-wsk1 << endl; cout << "większy: ((wsk1>wsk2) ? wsk1 : wsk2) << endl; Podstawy programowania - Tablice i wskaźniki
Podstawy programowania - Tablice i wskaźniki Typ void * Jest to wskaźnik uniwersalny, który może pokazywać na każdy typ danych Nie posiada jednak informacji o typie, a więc i o rozmiarze pokazywanego elementu Nie można jednak stosować wobec niego operatora * (odczytywać danej, na którą pokazuje) Nie można stosować do niego dodawania stałej, ani odejmować takich wskaźników (nieznany jest rozmiar elementu) Można do niego przypisać wartość dowolnego innego wskaźnika (niejawna konwersja typu) Przypisanie wartości wskaźnika void * do innego wskaźnika, a także odczyt pokazywanej danej są możliwe ale wymagają jawnej konwersji Stosuje się go do wskazywania danych o nieokreślonej postaci Podstawy programowania - Tablice i wskaźniki
Przykłady użycia void * void *adres; int *iwsk; float *fwsk; int i=5,j; float f; adres=&i; // tu nie trzeba konwersji iwsk=adres; // nie uda się iwsk=(int *) adres; // konwersja jawna - uda się! fwsk=(float *)adres; // to też się uda! j=*adres; // to się nie uda j=*iwsk; f=*fwsk; // ciekawe co jest w f ??? Podstawy programowania - Tablice i wskaźniki
Podstawy programowania - Tablice i wskaźniki Wskaźnik a tablica Nazwa tablicy (bez nawiasów []) jest wskaźnikiem do jej zerowego (początkowego) elementu Jest to wskaźnik, którego wartości nie można zmienić (wskaźnik stały). Można jednak używać go w różnych wyrażeniach Do wskaźnika wolno użyć nawiasów [], tak, jak gdyby był on zadeklarowany jako tablica jednowymiarowa Przykłady: int tab[10]; int *poczatek=tab; // wskazuje na tab[0] int *koniec=tab+9; // wskazuje na tab[9] int numer2= tab[2]; // tablica normalnie numer2= *(tab+2); // tab. jako wskaźnik numer2= *(poczatek+2); // wskaźnik normalnie numer2= poczatek[2]; // wskaźnik jako tab tab=&numer2; // błąd! wskaźnik stały Podstawy programowania - Tablice i wskaźniki
Typ char - stałe i zmienne Jest to typ znakowy, przydatny do przetwarzania tekstu, obsługi klawiatury i ekranu Pozwala na zapamiętanie jednego znaku w postaci tzw. kodu ASCII (jednobajtowa liczba całkowita) Domyślnie signed/unsigned zależy od kompilatora Wartość typu char można traktować w wyrażeniach jako liczbę całkowitą, konwersje znak - liczba odbywają się automatycznie Stałe znakowe znak pomiędzy parą znaków ' i ', np. 'A', '0', '@' znak \ i kod ASCII pomiędzy parą znaków ' i ', np. '\65', '\0' W przypadku przypisania liczby do zmiennej znakowej, następuje automatyczna konwersja na znak o podanym kodzie ASCII Znaki specjalne '\n'(new line),'\t'(tab), '\\', '\'', '\"' Podstawy programowania - Tablice i wskaźniki
Podstawy programowania - Tablice i wskaźniki Typy char[]i char * W celu zapisu tekstu tworzy się tablice znaków. W języku C wprowadzono konwencję, zgodnie z którą tekst kończy się znakiem '\0' (na który trzeba przewidzieć dodatkowy bajt w tablicy) - tzw. null-terminated string Praca z taką tablicą wymaga zwykle przetwarzania każdego znaku z osobna. Stosuje się do tego pętle, najczęściej pętlę for. W całości można jedynie dokonać inicjalizacji tablicy. Operacje na całym tekście można też wykonywać z udziałem wielu funkcji bibliotecznych. Zmienne typu char* wskazują na pojedyncze znaki, opisuje się też w ten sposób również cały tekst (wskazując pierwszy znak) Stałe tekstowe zapisuje się pomiędzy parą znaków " i " Są one typu char* Przydatne funkcje obsługujące teksty (zdefiniowane jako tablice lub wskaźniki) znaleźć można w pliku string.h Podstawy programowania - Tablice i wskaźniki
Przykłady pracy z tekstem char nazwisko[31]; // max. 30 znaków + '\0' char logo1[]= "ZPSB"; // wygodna inicjalizacja char logo2[]={'Z','P','S','B','\0'}; // tak też można char imie[20]="Michał"; // rozmiar jest większy nazwisko="Kowalski" // Błąd! stały wskaźnik! nazwisko[7]='a'; char *tekst=imie; *tekst='A'; //wstawimy "ABC" do imie *(tekst+1)='B'; tekst[2]='C'; tekst[3]=0; // koniec, konwersja z int cin >> nazwisko; // musi wystarczyć miejsca! cout << nazwisko<< endl; Podstawy programowania - Tablice i wskaźniki
Wskaźnik jako parametr funkcji (1) Funkcja może jako parametr przyjmować wskaźnik. Wartość wskaźnika jest kopiowana, więc wskazuje on na tę samą zmienną, co wskaźnik wysłany jako parametr Pozwala to funkcji na zmianę wartości tej zmiennej, co nie jest możliwe przy zwykłym przesyłaniu danych przez wartość Przykład: void zeruj_ujemne (int *liczba) { if (*a<0) a=0; } void main() { int a=-5; // tutaj a=-5 int *wsk=&a; zeruj_ujemne(wsk); // a tutaj już a=0 zeruj_ujemne(&a); // tak też można } Podstawy programowania - Tablice i wskaźniki
Wskaźnik jako parametr funkcji (2) Wskaźnik służy też zwykle do przekazywania tablicy (także tekstu) jako parametr. Często wykorzystuje się też wskaźniki, jeżeli funkcja generuje kilka różnych wartości wynikowych (zwrócić można tylko jedną) Przykład (zliczanie wartości dodatnich i niedodatnich w tablicy): void plusminus(int *tab, int n, int *plus, int *minus) { int pl=0, min=0; // zmienne lokalne for (int i=0; i<n; i++) if (tab[i]>0) pl++; // wskaźnik jako tabl. else min++; *plus=pl; // przekazanie wyników *minus=min; } // nie ma return, a funkcja zwraca wyniki void main() { int tablica[20], ileplus, ileminus; plusminus(tablica, 20, &ileplus, &ileminus); } Podstawy programowania - Tablice i wskaźniki
To już jest koniec++ Dziękuję za uwagę