Typy strukturalne (złożone)

Slides:



Advertisements
Podobne prezentacje
Tablice 1. Deklaracja tablicy
Advertisements

Katarzyna Szafrańska kl. II ti
C++ wykład 2 ( ) Klasy i obiekty.
C++ wykład 4 ( ) Przeciążanie operatorów.
Typy strukturalne Typ tablicowy.
Język C/C++ Funkcje.
Programowanie obiektowe
Programowanie obiektowe
Wzorce.
Język ANSI C Funkcje Wykład: Programowanie komputerów
Prowadzący: mgr inż. Elżbieta Majka
Języki programowania C++
Materiały do zajęć z przedmiotu: Narzędzia i języki programowania Programowanie w języku PASCAL Część 6: Tablice, rekordy, zbiory.
Struktury.
Tablice.
C++ wykład 2 ( ) Klasy i obiekty.
Dynamiczne struktury danych 1
Wykład 1: Wskaźniki Podstawy programowania Programowanie w C
Podstawy programowania PP – WYK2 Wojciech Pieprzyca.
Podstawy programowania PP – LAB5 Wojciech Pieprzyca.
Wykład 2 struktura programu elementy języka typy zmienne
Tablice tablica jest sekwencją elementów tego samego typu (prostego lub obiektowego) w Javie tablice są obiektami, a zmienne tablicowe przechowują referencję
nowe operatory & . (kropka) * operator rzutowy -> , (przecinek)
Podstawy programowania
Podstawy programowania II
Podstawy informatyki (4)
Podstawy informatyki 2013/2014
Wskaźnik może wskazywać na obiekt dowolnego typu. int * w; char * Wsk_Znak; float * Wskaz_Float; Przykład: Wskaźnik przechowuje adres obiektu wskazanego.
struct nazwa { lista składników }; Dostęp do składowych struktury Nazwa_Zmniennej_Strukturalnej. Nazwa_Składnika.
Podstawy programowania
TABLICE C++.
Łódź, 3 października 2013 r. Katedra Analizy Nieliniowej, WMiI UŁ Podstawy Programowania Złożona składnia języka C++
Podstawy programowania
Podstawy programowania w języku C i C++
Jerzy F. Kotowski1 Informatyka I Wykład 14 DEKLARATORY.
Andrzej Repak Nr albumu
Inicjalizacja i sprzątanie
Programowanie obiektowe Wykład 3 dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 1/21 Dariusz Wardowski.
Programowanie obiektowe Wykład 6 dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 1/14 Dariusz Wardowski.
Programowanie strukturalne i obiektowe
Podstawy informatyki 2013/2014
Przekazywanie parametrów do funkcji oraz zmienne globalne i lokalne
Programowanie obiektowe 2013/2014
Kurs języka C++ – wykład 9 ( )
Podstawy języka Instrukcje - wprowadzenie
Programowanie strukturalne i obiektowe C++
Programowanie strukturalne i obiektowe C++
Programowanie strukturalne i obiektowe C++
Zmienne i typy danych w C#
Kurs języka C++ – wykład 4 ( )
Typy liczbowe, zmienne, operatory Zajęcia 4. Zmienne Zmienna – to w programowaniu element programu, który może mieć przypisaną pewną wartość (wartość.
Tablice Zajęcia 8. Definicja Tablica (z ang. array) jest zmienną złożoną, która składa się z ciągu elementów tego samego typu. W pamięci komputera tablica.
Język C/C++ Instrukcje
Podstawy informatyki Tablice Łukasz Sztangret Katedra Informatyki Stosowanej i Modelowania Prezentacja przygotowana w oparciu o materiały Danuty Szeligi.
Wstęp do programowania wykład 3 Typy wyliczeniowe, tablice.
Wstęp do programowania Wykład 2 Dane, instrukcje, program.
Łukasz Sztangret Katedra Informatyki Stosowanej i Modelowania Prezentacja przygotowana w oparciu o materiały Danuty Szeligi i Pawła Jerzego Matuszyka Podstawy.
Podstawy informatyki Struktury Łukasz Sztangret Katedra Informatyki Stosowanej i Modelowania Prezentacja przygotowana w oparciu o materiały Danuty Szeligi.
K URS JĘZYKA C++ – WYKŁAD 3 ( ) Przenoszenie Składowe statyczne Funkcje wbudowane Argumenty domyślne.
P ASCAL Definicje, deklaracje, podstawowe instrukcje 1.
C++ mgr inż. Tomasz Turba Politechnika Opolska 2016.
 Formuła to wyrażenie algebraiczne (wzór) określające jakie operacje ma wykonać program na danych. Może ona zawierać liczby, łańcuchy znaków, funkcje,
C++ mgr inż. Tomasz Turba Politechnika Opolska 2016.
C++ mgr inż. Tomasz Turba Politechnika Opolska 2016.
Wskaźniki Elżbieta Labocha.
Programowanie I Rekurencja.
nowe operatory & . (kropka) * operator rzutowy -> , (przecinek)
Język C++ Typy Łukasz Sztangret Katedra Informatyki Stosowanej i Modelowania Prezentacja przygotowana w oparciu o materiały Danuty Szeligi i Pawła Jerzego.
Język C++ Tablice Łukasz Sztangret Katedra Informatyki Stosowanej i Modelowania Prezentacja przygotowana w oparciu o materiały Danuty Szeligi i Pawła Jerzego.
Zapis prezentacji:

Typy strukturalne (złożone) Typ tablicowy

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.

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=(a0,...,an-1) to x=(x[0],...,x[N-1]).

Wstęp Indeks tablicy. Wartość wyrażenia i zmienia się od 0 do N-1 (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.

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.

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 (grup bajtów) pamięci, w każdej z których można umieścić wartość typu double.

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. Słowo static oznacza, że zmienna tego typu znajduje się w pamięci operacyjnej cały czas, nawet wtedy, gdy jest niedostępna (instrukcja bieżąco wykonywana może znajdować się w innym bloku niż zmienna typu static).

Przykład #include <iostream> #include <stdlib.h> using namespace std; //wartości domyslne dla tablic static int a[4]; int c[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]<<" "; cout<<endl<<"tablica c:"; cout<<c[i]<<" "; int d[4]; cout<<endl<<"tablica d:"; for (int i=0; i<4;i++) cout<<d[i]<<" "; system ("Pause"); return 0; }

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.

Przykład #include <iostream> #include <conio.h> using namespace std; 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; }

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.

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.

Przykład #include <iostream> #include <conio.h> using namespace std; //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();

Przykład 2 #include <iostream> #include <conio.h> using namespace std; //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(); }

Przykład 3 #include <iostream> #include <conio.h> using namespace std; 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(); }

Przykład – inicjowanie liczbami losowymi #include <iostream> #include <stdlib.h> //funkcja rand() #include <conio.h> //funkcja getch() using namespace std; int main() { //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 0..100\n"; cout<<rand()%101<<endl; //dowolne z <10,100> liczby lowsowe cout<<"Liczby losowe z 10..100\n"; cout<<rand()%91+10<<endl; //dowolne rzeczywiste z <0,1> liczby lowsowe cout<<"Liczby losowe rzeczywiste z 0..1\n"; cout<<1.*rand()/(32767-1)<<endl; getch(); return 0; }

Losowość dla znaków #include <iostream> #include <conio.h> using namespace std; 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; }

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.

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.

Przykład #include <iostream> #include <conio.h> using namespace std; 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; //---------------------------------------------------------------------------

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.

Przykład using namespace std; #include <iostream> #include <conio.h> using namespace std; //--------------------------------------------------------------------------- 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;

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.

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

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.

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ą.

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.

Przykład #include <iostream> #include <stdlib.h> #include <conio.h> using namespace std; 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; }

Zagnieżdżanie struktur #include <iostream> #include <conio.h> using namespace std; //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=1234.56; 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; }

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.

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;

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.

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).

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.

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;

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 { ... };

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 przypadku – poprzednie+data śmierci.

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;

„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.

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 “->”.

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

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

Przykład Pytanie: jaki jest typ zmiennej *p (wiadomo) i p (??) //--------------------------------------------------------------------------- #include <iostream> #include <conio.h> using namespace std; int main() { int *p; //*p=7; - bład wykonania //p=7; //blad kompilacji, dlaczego? cout <<"*p="<<*p<<' '; cout <<"p="<<p<<' '; getch(); return 0; } //---------------------------------------------------------------------- Pytanie: jaki jest typ zmiennej *p (wiadomo) i p (??) p to adres, *p to zawartość adresu p

Wskaźnik i pobranie adresu //--------------------------------------------------------------------------- #include <iostream> #include <conio.h> using namespace std; int main() { 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. k to adres, n to zmienna, *k zawartość adresu (n), &n adres zmiennej n ( k)

Wskaźniki – przykład podsumowujący elementarne operacje #include <iostream> #include <conio.h> using namespace std; //Wskaźniki int main() { int liczba = 9; int *wsk_liczba; 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; }

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

Zmiana danych //--------------------------------------------------------------------------- #include <iostream> #include <conio.h> using namespace std; int main() { int i=10; int* wi=&i; cout<<"i="<<i<<endl; *wi=50; cout<<"i po zmianie="<<i<<endl; getch(); return(0); }

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? Błąd ten jest nie dopuszczalny i do tego trudno go wykryć.

Arytmetyka wskaźników int main() { //tablice deklaracia inicjalizacja double waga[5] = {55.3, 747.8, 1001.2, 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; } //---------------------------------------

Wyświetlanie tablic #include <iostream> #include <conio.h> using namespace std; int main() { //tablice deklaracia inicjalizacja double waga[5] = {55.3, 747.8, 1001.2, 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; } //---------------------------------------

Tablice są wskaźnikami #include <iostream> #include <conio.h> using namespace std; 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; } //---------------------------------------

Tablice dwuwymiarowe a wskaźniki #include <iostream> #include <conio.h> using namespace std; 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; } //---------------------------------------

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”;}

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; }

Dostęp do struktury za pomocą wskaźników #include <iostream> #include <conio.h> using namespace std; 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); } //---------------------------------------

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

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.

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.