Pobieranie prezentacji. Proszę czekać

Pobieranie prezentacji. Proszę czekać

Typy strukturalne Typ tablicowy.

Podobne prezentacje


Prezentacja na temat: "Typy strukturalne Typ tablicowy."— Zapis prezentacji:

1 Typy strukturalne Typ tablicowy

2 Wstęp Pojęcie ciągu. Dla danego zbioru możemy utworzyć nowy zbiór AN, którego elementy są ciągami: x=(a0,...,aN-1) złożonymi dokładnie z N elementów, z których każdy element należy do A. Elementy mogą się powtarzać. Każdy taki ciąg będzie nazywany tablicą N elementów z A.

3 Wstęp Ze zbiorem AN wiążemy N operatorów wybierających poszczególne składowe z tablicy: [i]:AN->A dla 0<=i<N, takich, że x[i]=ai. Np. [0] wybiera pierwszy element, [1] drugi itd. Zatem jeśli x=(a1,...,an) to x=(x[0],...,x[N-1]).

4 Wstęp Indeks tablicy. Wartość wyrażenia i zmienia się od 0 do N (w omawianym przypadku) i jest nazywana indeksem tablicy. Ważną cechą indeksu jest to, że oblicza się go obliczając wartość odpowiedniego wyrażenia. Dla danego indeksu i wartości a z A możemy zmienić odpowiednią pozycję tablicy za pomocą przypisania x[i]=a.

5 Wstęp Podsumowując: Tablica jest strukturą danych, złożoną z określonej liczby elementów tego samego typu. Elementy tablicy mogą być typu int, double, float, short int, long int, char, wskaźnikowego, struct, union; mogą być również tablicami oraz obiektami klas. Deklaracja tablicy składa się ze specyfikatora (określnika) typu, identyfikatora i wymiaru. Wymiar tablicy, który określa liczbę elementów zawartych w tablicy, jest ujęty w parę nawiasów prostokątnych “[]” i musi być większy lub równy jedności. Jego wartość musi być wyrażeniem stałym typu całkowitego, możliwym do obliczenia w fazie kompilacji; oznacza to, że nie wolno używać zmiennej dla określenia wymiaru tablicy. Parę nawiasów “[]”, poprzedzonych nazwą tablicy, np. “Arr[]”, nazywa się deklaratorem tablicy.

6 Wstęp Elementy tablicy są dostępne poprzez obliczenie ich położenia w tablicy. Taka postać dostępu jest nazywana indeksowaniem. Np. zapis: double tab[4]; deklaruje tablicę 4 elementów typu double: tab[0], tab[1], tab[2] i tab[3], gdzie tab[i] są nazywane zmiennymi indeksowanymi. Inaczej mówiąc, zmienne tab[0], ..., tab[3] będą sekwencją czterech kolejnych komórek pamięci, w każdej z których można umieścić wartość typu double.

7 Deklarowanie tablic Deklarację tablicy można powiązać z nadaniem wartości inicjalnych jej elementom. Tablicę można zainicjować na kilka sposobów: Umieszczając deklarację tablicy na zewnątrz wszystkich funkcji programu. Taka tablica globalna (ewentualnie poprzedzona słowem kluczowym static) zostanie zainicjowana automatycznie przez kompilator. Np. każdy element tablicy globalnej o deklaracji double tab[3]; zostanie zainicjowany na wartość 0.0. Deklarując tablicę ze słowem kluczowym static w bloku funkcji. Jeżeli nie podamy przy tym wartości inicjalnych, to uczyni to, jak w poprzednim przypadku, kompilator. Czyli elementy liczbowe tablicy będą wyzerowane.

8 Przykład #include <iostream.h> #include <conio.h> //wartości domyslne dla tablic static int a[4]; int main() { cout<<"tablica a:"; for (int i=0;i<4;i++) cout<<a[i]<<" "; static int b[4]; cout<<endl<<"tablica b:"; cout<<b[i]<<" "; int d[4]; cout<<endl<<"tablica d:"; for (int i=0; i<4;i++) cout<<d[i]<<" "; getch(); return 0; }

9 Deklarowanie tablic - cd
Definiując tablicę, tj. umieszczając po jej deklaracji wartości inicjalne, poprzedzone znakiem “=”. Wartości inicjalne, oddzielone przecinkami, umieszcza się w nawiasach klamrowych po znaku “=”. Np. tablicę tab[4] można zainicjować instrukcją deklaracji: double tab[4] = { 1.5, 6.2, 2.8, 3.7 }; W deklaracji inicjującej można pominąć wymiar tablicy: double tab[] = { 1.5, 6.2, 2.8, 3.7 }; W tym przypadku kompilator obliczy wymiar tablicy na podstawie liczby elementów inicjalnych. Deklarując tablicę, a następnie przypisując jej elementom pewne wartości w kolejnych instrukcjach przypisania, np. umieszczonych w pętli.

10 Przykład #include <iostream.h> #include <conio.h> int main() { double tab[] = { 1.5, 6.2, 2.8, 3.7 }; for (int i=0; i<4;i++) cout<<tab[i]<<" "; getch(); return 0; }

11 Tablice „stałe” Podobnie jak stałe typów wbudowanych, można zdefiniować tablicę o elementach stałych, np. const int tab[4] = { 10, 20, 30, 40 }; lub const int tab[] = { 10, 20, 30, 40 }; W takim przypadku zmienne indeksowane stają się stałymi symbolicznymi, których wartości nie można zmieniać żadnymi instrukcjami przypisania.

12 Inicjowanie tablic zadanymi wartościami
Jeżeli w definicji tablicy podaje się wartości inicjalne składowych, to kompilator inicjuje tablicę według rosnących adresów jej elementów. W przypadku gdy liczba wartości inicjalnych jest mniejsza od maksymalnego indeksu, podanego w deklaratorze ([ ]) tablicy, zostaną zainicjowane początkowe składowe, a pozostałe kompilator zainicjuje na 0. Błędem syntaktycznym jest podanie większej od wymiaru tablicy liczby wartości inicjalnych.

13 Przykład #include <vcl.h> #pragma hdrstop
#include <iostream.h> #include <conio.h> #pragma argsused //nadawanie wartosci według pewnego algorytmu void main() { int i; const int WYMIAR = 10; int tab[WYMIAR]; for (i = 0; i < WYMIAR; i++) { tab[i] = i; cout << "tab["<< i << "]= "<< tab[i] << endl; } cout<<"inaczej:"<<tab; //wyjscie nieformatowe getch();

14 Przykład 2 #include <iostream.h> #include <conio.h>
//nadawanie wartosci tablicom znakowym void main() { const int BUF = 10; char a[BUF] ="ABCDEFGHIJ"; // a[BUF]-tablica 10 znakow: a[0],a[1],...,a[9] /* Dopuszczalne jest zainicjowanie: char a[BUF] ={'A','B','C','D','E','F','G','H','I','J'}; */ cout <<a<< endl; getch(); }

15 Przykład 3 #include <iostream.h> #include <conio.h>
void main() { const int BUF = 10; char znak; char a[BUF] = {'A','B','C','D','E','F','G','H','I','J'}; // a[BUF]-tablica 10 znakow: a[0],a[1],...,a[9] for (znak = 'A'; znak <= 'J'; znak++) cout << a[znak - 65] << endl; getch(); }

16 Przykład – inicjowanie liczbami losowymi
#include <iostream.h> //operacje wejscia/wyjscia #include <stdlib.h> //funkcja rand() i randomize() #include <conio.h> //funkcja getch() int main(int argc, char* argv[]) { // dla testów zostawiamy komentarz, dla programu docelowego usuwamy // randomize(); //dowolne >=0 liczby losowe cout<<"Liczby losowe>=0\n"; for (int i=0;i<10;i++) cout<<rand()<<endl; //dowolne z <0,100> liczby lowsowe cout<<"Liczby losowe z \n"; cout<<rand()%101<<endl; //dowolne z <10,100> liczby lowsowe cout<<"Liczby losowe z \n"; cout<<rand()%91+10<<endl; //dowolne rzeczywiste z <0,1> liczby lowsowe cout<<"Liczby losowe rzeczywiste z 0..1\n"; cout<<1.*rand()/( )<<endl; getch(); return 0; }

17 Losowość dla znaków #include <iostream.h> //operacje wejscia/wyjscia #include <stdlib.h> //funkcja rand() i randomize() #include <conio.h> //funkcja getch() int main() { char a[10]; for (int i=0;i<10;i++) a[i]=char(65+rand()%27); cout<<a[i]<<endl; getch(); return 0; }

18 Tablice wielowymiarowe - wstęp
Ponieważ elementy tablicy mogą być tablicami, możliwe jest deklarowanie tablic wielowymiarowych. Np. zapis: int tab[4][3]; deklaruje tablicę o czterech wierszach i trzech kolumnach (jest to umowne). W tym przypadku dopuszcza się notacje: tab - tablica 12-elementowa, tab[i] - tablica 3-elementowa, w której każdy element jest tablicą 4-elementową, tab[i][j] - element tablicy typu int.

19 Tablice wielowymiarowe - wstęp
Inaczej mówiąc, tablica tab[4][3] jest tablicą złożoną z czterech elementów tab[0], tab[1], tab[2] i tab[3], przy czym każdy z tych czterech elementów jest tablicą trójelementową liczb całkowitych. Podobnie jak dla tablic jednowymiarowych, identyfikator tab jest niejawnie przekształcany we wskaźnik (będzie później) do pierwszego elementu tablicy, czyli do pierwszej spośród czterech tablic trójelementowych.

20 Przykład #include <iostream.h> #include <conio.h> int main() { int a[10][10]; int k=0; for (int i=0;i<10;i++) for (int j=0;j<10;j++) a[i][j]=rand()%101; } cout<<"Tablica liczb calkowitych"; cout<<"\n"; cout<<a[i][j]<<" "; getch(); return 0; //

21 Tablice wielowymiarowe - inicjowanie
Tablice wielowymiarowe inicjuje się podobnie, jak jednowymiarowe, zamykając wartości inicjalne w nawiasy klamrowe. Jednak ze względu na zapamiętywanie wierszami przewidziano dodatkowe, zagnieżdżone nawiasy klamrowe, w których umieszcza się wartości inicjalne dla kolejnych wierszy.

22 Przykład #include <iostream.h> //operacje wejscia/wyjscia
#include <stdlib.h> //funkcja rand() i randomize() #include <conio.h> //funkcja getch() // int main() { int tab[4][2] = // W [4] mozna opuscic 4 { { 1, 2 }, // inicjuje tab[0], tj. tab[0][0] i tab[0][1] { 3, 4 }, // inicjuje tab[1] { 5, 6 }, // inicjuje tab[2] { 7, 8 } // inicjuje tab[3] }; for (int i = 0; i < 4; i++) cout<<" tab["<<i<<"][0]: "<<tab[i][0]<<'\t'; cout<<"tab["<<i<<"][1]: "<<tab[i][1]<<'\n'; } getch(); return 0;

23 Uwagi Wynik byłby identyczny, gdyby definicja tablicy miała postać:
int tab[4][2] = { 1, 2, 3, 4, 5, 6, 7, 8 }; Podobnie jak dla tablic jednowymiarowych można podać mniejszą od stopnia tablicy (tj. iloczynu wszystkich jej wymiarów) liczbę wartości inicjalnych. Wówczas ta część elementów tablicy, dla której zabraknie wartości inicjalnych, zostanie zainicjowana zerami. Tę własność należy również mieć na uwadze przy opuszczaniu zagnieżdżonych nawiasów klamrowych. Np. wykonanie instrukcji deklaracji int tab[4][2] = { 1, 2, 3, 4, 5, 6 }; lub int tab[4][2] = {{ 1, 2 },{ 3, 4},{5, 6 } }; Nada wartości inicjalne pierwszym trzem wierszom i spowoduje wyzerowanie czwartego wiersza.

24 Uwagi - cd Natomiast wykonanie instrukcji deklaracji
int tab[4][2] = {{ 1 },{ 2 }, { 3 },{4 } }; spowoduje wyzerowanie drugiej kolumny macierzy tab[4][2].

25 Struktury - wstęp Mając dane zbiory A1, ..., AN możemy utworzyć iloczyn kartezjański A1 x...x AN z przykładowym elementem x=(a1,...,aN), gdzie ajAj dla 1<=j<=N. Każdy taki ciąg nazywamy strukturą (rekordem). W tym przypadku można wybrać operator pozwalający wybrać poszczególne wartości. Jest to operator „.” (kropka) zdefiniowany następująco: .sj:A1x...xAN->Aj, gdzie sj jest identyfikatorem przyjętym dla Aj. Podany operator wybiera z rekordu x=(a1,...,aN) składową j x.sj=aj należącą do Aj.

26 Tablice a Struktury Różnica między tablicami, a strukturami polega na tym, że w tablicy wszystkie elementy są tego samego typu. W przypadku tablicy indeksy są wartościami skalarnymi, więc można je przetwarzać w ustalonym porządku (instrukcja dla), albo otrzymać wyliczając pewne wyrażenie. Natomiast w przypadku struktury identyfikatory sj stanowią nieuporządkowany zbiór różnych nazw. Nie tworzą one ustalonego, stałego typu danych. Składowe struktur należy więc nazywać indywidualnie, nie można się do nich kolejno odwoływać a tylko indywidualnie. Struktury stosujemy wtedy, gdy w programie chcemy zarządzać złożoną strukturą danych, dostęp do takich danych jest poprzez jedną zmienną.

27 Struktury Struktura jest jednostką syntaktyczną grupującą składowe różnych typów, zarówno podstawowych, jak i pochodnych. Ponadto składowa struktury może być tzw. polem bitowym. Struktury są deklarowane ze słowem kluczowym struct. Deklaracja referencyjna struktury ma postać: struct nazwa; zaś jej definicja struct nazwa { /*...*/ }; gdzie nazwa jest nazwą nowo zdefiniowanego typu. Na końcu struktury dajemy średnik po nawiasie klamrowym.

28 Przykład #include <iostream.h> //operacje wejscia/wyjscia
#include <stdlib.h> //funkcja rand() i randomize() #include <conio.h> //funkcja getch() struct skrypt { char *tytul; //tablica znaków char *autor;//tablica znaków float cena; long int naklad; char status; }; int main() { skrypt ks; ks.autor ="Jan Kowalski"; ks.cena = 12.55; ks.naklad = 50000; ks.status = 'A'; cout <<"stary.autor ="<<ks.autor << '\n'; cout <<"stary.cena ="<<ks.cena << '\n'; cout <<"stary.naklad ="<<ks.naklad << '\n'; cout <<"stary.status ="<<ks.status << '\n'; getch(); return 0; }

29 Zagnieżdżanie struktur
#include <iostream.h> #include <conio.h> //Uwaga - struktura jest zdefiniowana globalnie struct adres { string ulica; int numer; }; //uwaga, srednik musi byc struct osoba string nazwisko; adres adr; //w strukturze osoba korzystamy ze struktury adres double brutto; }; int main(int argc, char* argv[]) osoba o1; o1.nazwisko="Abacki"; o1.adr.ulica="3-go Maja"; o1.adr.numer=23; o1.brutto= ; cout<<"\nRekord osoba\n"; cout<<"nazwisko="<<o1.nazwisko; cout<<"\nulica="<<o1.adr.ulica; cout<<"\nnumer domu="<<o1.adr.numer; cout<<"\nbrutto="<<o1.brutto; getch(); return 0; }

30 Uwaga – zgodność typów Każda deklaracja struktury wprowadza nowy, unikatowy typ, np. struct s1 { int i ; }; struct s2 { int j ; }; są dwoma różnymi typami; zatem w deklaracjach s1 x, y ; s2 z ; zmienne x oraz y są tego samego typu s1, ale x oraz z są różnych typów.

31 Uwagi - cd Wobec tego przypisania są poprawne, podczas gdy są błędne.
x = y; y = x; są poprawne, podczas gdy x = z; z = y; są błędne. Dopuszczalne są natomiast przypisania składowych o tych samych typach, np. x.i = z.j;

32 Pola bitowe - wstęp Obszar pamięci zajmowany przez strukturę jest równy sumie obszarów, alokowanych dla jej składowych. Jeżeli np. struktura ma trzy składowe typu int, a implementacja przewiduje 2 bajty na zmienną tego typu, to reprezentacja struktury w pamięci zajmie 6 bajtów. Dla dużych struktur (np. takich, których składowe są dużymi tablicami) obszary te mogą być znacznej wielkości. W takich przypadkach możliwe jest ściślejsze upakowanie pól struktury poprzez zdefiniowanie tzw. pól bitowych, które zawierają podaną w deklaracji liczbę bitów. W pamięci komputera pole bitowe jest zbiorem sąsiadujących ze sobą bitów w obrębie jednej jednostki pamięci zdefiniowanej w implementacji, a nazywanej słowem.

33 Pola bitowe - wstęp Rozmieszczenie w pamięci struktury z polami bitowymi jest także zależne od implementacji. Jeżeli dane pole bitowe zajmuje mniej niż jedno słowo (dwa bajty – teraz raczej operuje się bajtami), to następne pole może być umieszczone albo w następnym słowie albo częściowo w wolnej części pierwszego słowa, a pozostałe bity w następnym słowie. Przy tym, zależnie od implementacji, alokacja pola bitowego może się zaczynać od najmniej znaczącego lub od najbardziej znaczącego bitu słowa (słowo to najczęściej dwa bajty, stąd mowa o bardziej znaczącym bicie i mniej znaczącym).

34 Deklaracja pól bitowych
Deklaracja składowej będącej polem bitowym ma postać: typ nazwa_pola : wyrażenie; gdzie: typ oznacza typ pola i musi być jednym z typów całkowitych, tj. char, short int, int, long int ze znakiem (signed) lub bez (unsigned) oraz enum; występujące po dwukropku wyrażenie określa liczbę bitów zajmowaną przez dane pole.

35 Przykład wykorzystania
Pola bitowe zachowują się jak małe liczby całkowite i mogą występować w wyrażeniach arytmetycznych, w których przeprowadza się operacje na liczbach całkowitych. Przykładowo, deklarację struct sygnalizatory { unsigned int sg1 : 1; unsigned int sg2 : 1; unsigned int sg3 : 1; } s; możemy wykorzystać do włączania lub wyłączania sygnalizatorów s.sg1 = 1; s.sg2 = 0; s.sg3 = 1; lub testowania ich stanu if (s.sg1 == 0 && s.sg3 == 0) s.sg2 = 1;

36 Unie - wstęp Unia, podobnie jak struktura, grupuje składowe różnych typów. Jednak - w odróżnieniu od struktury - tylko jedna ze składowych unii może być “aktywna” w danym momencie. Wynika to stąd, że każda ze składowych unii ma ten sam adres początkowy w pamięci, zaś obszar pamięci zajmowany przez unię jest równy rozmiarowi jej największej składowej. Definicja unii ma postać: union nazwa { ... };

37 Unie - wstep Inna, alternatywną nazwą dla unii mogą być rekordy z wariantami. Odpowiadają one sytuacji, że w danym momencie można wybrać tylko jeden wariant. Przykładem może stan cywilny: kawaler/panna, żonaty/mężatka, rozwodnik/rozwódka i wdowiec/wdowa. Dla każdego z tych wariantów potrzebujemy różne dane: Dla I przypadku – nic, Dla II przypadku – data zawarcia związku+miejscowość, gdzie był ślub, Dla III przypadku – jw.+data rozwodu i ew. miejscowość, gdzie był udzielony rozwód, Dla IV przyapdku – ew. poprzednie+data śmierci.

38 Inicjowanie unii Unię można zainicjować albo wyrażeniem prostym tego samego typu, albo ujętą w nawiasy klamrowe wartością pierwszej zadeklarowanej składowej. Np. unię uu można zainicjować deklaracją test uu = { 1 }; Podobnie jak dla struktur, można stosować instrukcję przypisania dla unii tego samego typu, np. test uu, uu1, uu2; uu2 = uu1 = uu;

39 „Wyjątkowe” definicje
W definicji unii można pominąć nazwę po słowie kluczowym union, a deklaracje zmiennych umieścić pomiędzy zamykającym nawiasem klamrowym a średnikiem, np. union { int i; char *p; } uu, Powyższa definicja również tworzy unikatowy typ. Ponieważ typ występuje tutaj bez nazwy, taką definicję stosuje się wtedy, gdy unia ma być wykorzystana tylko jeden raz. Natomiast dostęp do składowych jest taki sam, jak poprzednio.

40 Unie anonimowe Specyficzną dla języka C++ jest unia bez nazwy i bez deklaracji zmiennych, o składni: union { wykaz-składowych }; Taki zapis, nazywany unią anonimową, nie tworzy nowego typu, a jedynie deklaruje szereg składowych, które współdzielą ten sam adres w pamięci. Ponieważ unia nie ma nazwy, jej elementy są dostępne bezpośrednio - nie ma potrzeby stosowania operatorów “.” i “->”.

41 Przykład // #include <vcl.h> #pragma hdrstop #pragma argsused #include <iostream.h> #include <conio.h> int main() { union { int i; char *p; } ; i = 14; cout << i << endl; p = "abcd"; cout << p << endl; getch(); return 0; }

42 Wskaźniki Co oznacza deklaracja?:
int i;//rezerwuje miejsce w pamięci dla typu całkowitego Wskaźnik (ang. pointer) to specjalny rodzaj zmiennej, w której zapisany jest adres w pamięci komputera, tzn. wskaźnik wskazuje miejsce, gdzie zapisana jest jakaś informacja (stąd nazwa zmienna wskaźnikowa). Adres to pewna liczba całkowita, jednoznacznie definiująca położenie pewnego obiektu (czyli np. znaku, liczby, struktury czy tablicy) w pamięci komputera. Wskaźnik ma ścisłą kontrolę typów i z tego powodu nie tylko wskazujemy miejsce, które zajmuje zmienna, ale także ile bajtów ta zmienna potrzebuje, Definicja zmiennej typu wskaźnikowego: typ *id_zmiennej; Przykład: int *i float *x; float *x,y; /deklaruje wskaźnik i zmienną typu float int *n1; int * n2; int* n3; int*n4; //różne sposoby deklarowania wskaźników

43 Przykład Pytanie: jaki jest typ zmiennej *p (wiadomo) i p (??)
// #include <vcl.h> #pragma hdrstop #include <iostream.h> #include <conio.h> // #pragma argsused int main(int argc, char* argv[]) { int *p; *p=7; //p=7; //blad, dlaczego? cout <<"*p="<<*p<<' '; cout <<"p="<<p<<' '; getch(); return 0; } Pytanie: jaki jest typ zmiennej *p (wiadomo) i p (??)

44 Wskaźnik i pobranie adresu
// #include <vcl.h> #include <iostream.h> #include <conio.h> #pragma hdrstop #pragma argsused int main(int argc, char* argv[]) { int n=10; // deklaracja i definicja zmienej int *k; //deklaracja wskaźnika na int k=&n; //przypisanie zmiennej, która przechowuje adres adresu zmiennej n cout<<"*k="<<*k; //nie powinno dziwić, że pojawi się 10 cout<<"&n="<<&n;// a co to? getch(); return 0; } operator & służy do pobrania adresu miejsca w pamięci, gdzie egzystuje zmienna – jest nazywany często referencją a operator * jest nazywany operatorem dereferencji lub wskaźnikiem.

45 Wskaźniki – przykład podsumowujący elementarne operacje
#include <vcl.h> #pragma hdrstop #include <iostream.h> #include <conio.h> #pragma argsused //Wskaźniki int main() { int liczba = 9; int *wsk_liczba; /*--deklaracja wskaźnika na int--*/ wsk_liczba = &liczba; /* przypisanie wskaźnikowi adresu int */ //dwa sposoby wyświetlenia wartości liczba cout << "Zmienna liczba = " << liczba << "\ni *wsk_liczba jako zmienna liczba = " << *wsk_liczba << endl; //dwa sposoby wyświetlenia adresu zmiennej cout << "Adres liczby = " << &liczba << "\ni wsk_liczba jako adres liczby = " << wsk_liczba //zmiana wartości za pomocą wskaźnika *wsk_liczba = *wsk_liczba + 1; cout << "Liczba = " << liczba; getch(); return 0; }

46 Inicjowanie wskaźnika
int *wsk = NULL; int *wsk = 0; NULL oznacza element nie istniejący

47 Zmiana danych // #include <vcl.h> #include <iostream.h> #include <conio.h> #pragma hdrstop #pragma argsused int main(int argc, char* argv[]) { int i=10; int* wi=&i; cout<<"i="<<i<<endl; *wi=50; cout<<"i po zmianie="<<i<<endl; getch(); return(0); }

48 Złe użycie wskaźnika Podczas używania wskaźników można popełnić błąd przy tworzeniu i używaniu wskaźnika. Jeżeli nie zadbamy o przypisanie wskaźnikowi adresu. int *wsk_liczba; *wsk_liczba = 373; Jest to błąd, ponieważ nie wiemy co oznacza komórka o adresie 373. Co jeżeli jednak adres ten będzie już zajęty przez program? Taka sytuacja może spowoduje, iż nie będzie można było zapisać nic w miejsce wskazane przez wsk_liczba. Błąd ten jest nie dopuszczalny i do tego trudno go wykryć.

49 Arytmetyka wskaźników
int main() { //tablice deklaracia inicjalizacja double waga[5] = {55.3, 747.8, , 5.2, 6.4}; short odliczanie[4] = {3, 2, 1, 0}; //wskaźniki double *wsk_waga = waga; //wskaźnik=tabela short *wsk_odliczanie = &odliczanie[0]; //Wyświetlanie adresu i wartości wskaźnika wsk_waga cout << "wsk_waga = " << wsk_waga << ", *wsk_waga = " << *wsk_waga << endl << "Dodawanie wsk_waga + 1 "; wsk_waga += 1; cout << "\nTeraz wsk_waga = " << wsk_waga << ", *wsk_waga = " << *wsk_waga << endl << endl; //Wyświetlanie adresu i wartości wskaźnika wsk_odliczanie cout << "wsk_odliczanie = " << wsk_odliczanie << ", *wsk_odliczanie = " << *wsk_odliczanie << "Dodawanie wsk_odliczanie + 1 "; wsk_odliczanie += 1; cout << "\nTeraz wsk_odliczanie = " << wsk_odliczanie << ", *wsk_odliczanie = " << *wsk_odliczanie << endl getch(); return 0; } //

50 Wyświetlanie tablic #include <vcl.h> #include <iostream.h>
#include <conio.h> #pragma hdrstop // #pragma argsused int main() { //tablice deklaracia inicjalizacja double waga[5] = {55.3, 747.8, , 5.2, 6.4}; short odliczanie[4] = {3, 2, 1, 0}; double *wsk_waga = waga; //wskaźnik=tabela short *wsk_odliczanie = &odliczanie[0]; //Wyświetlanie tablic cout << "\nPodobienstwa tablic i wskaznikow\n" << "Pierwszy element tab waga[0] = " << waga[0] << endl << "Drugi element tab odliczanie[1] = " << odliczanie[1] << endl << endl; //Wyświetlanie zapisu wskaźnikowego cout << "Pierwszy element tab waga " "z uzyciem wskaznika *waga = " << *waga << endl << "Drugi element tab odliczanie " "z uzyciem wskaznika *(odliczanie + 1) = " << *(odliczanie + 1) << endl << endl << endl; getch(); return 0; } //

51 Tablice są wskaźnikami
#include <vcl.h> #include <iostream.h> #include <conio.h> #pragma hdrstop // #pragma argsused int main() { int a[20]; for (int i=0; i<20; i++) a[i]=rand()%100; cout<<"a"<<i<<"="<<*(a+i)<<"*******"<<a[i]<<endl; getch(); return 0; } //

52 Tablice dwuwymiarowe a wskaźniki
#include <vcl.h> #include <iostream.h> #include <conio.h> #pragma hdrstop // #pragma argsused int main() { int a[20][20]; for (int i=0; i<20; i++) for (int j=0; j<20; j++) a[i][j]=rand()%100; if (i==j) cout<<"a"<<i<<","<<j<<"="<<*(*(a+i)+j)<<"*******"<<a[i][j]<<endl; getch(); return 0; } //

53 Tablice dynamiczne Dynamiczna alokacja tablicy jednowymiarowej:
int * tablica;tablica = new int[rozmiar];  gdzie rozmiar jest wyrażeniem typu int.lubint *tablica= new int [rozmiar];  Każdą zadeklarowaną tablicę należy „zlikwidować” tzn. zwolnić zajętą przez nią pamięć:  delete [] tablica; Uwaga! Jeśli w programie zamierzamy kreować dużo obiektów dynamicznych korzystając z zapasu pamięci to musimy pamiętać, że pamięć się w końcu wyczerpie. Trzeba się z tym liczyć i sprawdzać czy operacja się powiodła  float *wsk *wsk=new float[8192]; if(!wsk) //czyli if(wsk==NULL) { cout<<”Pamięć się wyczerpała”;}

54 Tablice dynamiczne dwuwymiarowe
//Przykład z ćwiczeń main() { int **tablica; int l_wierszy=5,l_kolumn=6; tablica=new int*[ l_wierszy];  for(int i=0;i< l_wierszy;i++) tablica[i]=new int[l_kolumn];  //I teraz już mamy tablicę tablica[5][6] i możemy ją wypełnić for(int i=0;i< l_wierszy; i++) for(int j=0;j< l_kolumn; j++) cin>>tablica[i][j];  cout<<tablica[3][4];  for(int i=0;i< l_wierszy;i++)// zwolnienie pamięci delete []tablica [i]; delete []tablica; }

55 Dostęp do struktury za pomocą wskaźników
#include <vcl.h> #include <iostream.h> #include <conio.h> #pragma hdrstop // #pragma argsused int main() { struct punkt float x; float y; char kolor; }; punkt p; p.x=10; p.y=20; p.kolor='r'; punkt* wsk=&p; cout<<"(*wsk).x="<<(*wsk).x<<endl; (*wsk).x=99; cout<<"p.x="<<p.x<<endl; wsk->x=9; getch(); return(0); } //

56 Zasady korzystania ze wskaźników
Należy inicjalizować wskaźnik możliwie jak najwcześniej, a jeśli to nie możliwe to ustawić go na NULL Ustawiając wskaźnik na tablicę należy uważać, by nie przekroczyć jej zakresu Powtórne zwolnienie tego samego obszaru pamięci przeważnie jest tragiczne w skutkach Błędnie ustawione wskaźniki stanowią ogromne zagrożenie i często owocuje to załamaniem programu

57 Co nam dają wskaźniki Wskaźniki dają możliwość zarządzania pamięcią – inne języki programowania nie zawsze oferują takie możliwości, Prawdziwa siła wskaźników polega na wykorzystywaniu struktur dynamicznych: Tablice – były przykłady, Struktury to listy, kolejki, stosy itd.

58 Typy danych - podsumowanie
Komu czego służą typy danych?, Typ danych określa zbiór wartości+operacje, Typy standardowe i niestandardowe, Standardowe to: typy liczbowe (całkowite i rzeczywiste), znakowe, wyliczeniowe, Niestandardowe to: tablicowe, strukturalne, plikowe i funkcyjne.


Pobierz ppt "Typy strukturalne Typ tablicowy."

Podobne prezentacje


Reklamy Google