złoŜone typy danych
tablice
struktury
unie
wyliczenia
wskaźniki
C/C++ - ZłoŜone typy danych, wskaźniki Tablice
Tablice są zbiorami elementów tego samego typu. Dostęp do elementów tablicy odbywa się poprzez indeks (adres w tablicy).
Postać deklaracji tablicy jednowymiarowej (wektora) jest następująca: int wektor_liczb[20];
typ elementów
nazwa
rozmiar
tablicy
Indeksowanie tablic zaczyna się od indeksu o wartości 0
C/C++ - ZłoŜone typy danych, wskaźniki Tablice c.d.
Zapis do tablicy i odczyt z tablicy jednowymiarowej
float wektor_liczb[20];
float liczba_rzeczywista;
wektor_liczb[0]=1.34;
liczba_rzeczywista=wektor_liczb[0];
Utworzenie wektora zainicjowanego
char tabl_znakow[10]={’a’,’b’,’c’,’d’};
Tablice jednowymiarowe wykorzystywane są bardzo często do przechowywania znaków (łańcuchów znaków).
C/C++ - ZłoŜone typy danych, wskaźniki Tablice c.d.
Deklaracja tablicy dwuwymiarowej ma postać:
int tablica_liczb[5][10];
typ elementów
nazwa
liczba
liczba
tablicy
wierszy
kolumn
W dostępie do elementu tablicy pierwszy indeks oznacza numer wiersza, a drugi – numer kolumny.
C/C++ - ZłoŜone typy danych, wskaźniki Tablice c.d.
Zapis do tablicy i odczyt z tablicy dwuwymiarowej
const unsigned char N=5,M=10;
float tabl_liczb[N][M];
float liczba_rzeczywista;
tabl_liczb[0][9]=3.33;
liczba_rzeczywista=tabl_liczb[0][9];
W powyŜszym przykładzie operacje dotyczą elementu tablicy połoŜonego w pierwszym wierszu (indeks 0) i ostatniej kolumnie (indeks 9).
W linii deklaracji tablicy rozmiar tablicy musi być znany (lub musi się dać ustalić) w czasie kompilacji.
Utworzenie tablicy zainicjowanej
int tablica[2][3]={{1,2,3},
{4,5,6}};
C/C++ - ZłoŜone typy danych, wskaźniki Tablice - tablice wielowymiarowe
W języku C++ moŜliwe jest deklarowanie tablic o wielu wymiarach.
Postać deklaracji tablicy N-wymiarowej jest następująca: typ nazwa[ wN]...[ w2][ w1]
Deklaracja tablicy trójwymiarowej
short int tabl_liczb[5][10][10];
Uwaga:
W języku C nie jest prowadzona kontrola zakresów, zatem moŜliwe jest wykonywanie operacji na elementach spoza tablicy (na innych danych, a nawet na kodzie). przykład kontr_zakr_tab
Deklarowane do tej pory tablice są tablicami statycznymi. Oznacza to, Ŝe w momencie deklaracji tablicy następuje zarezerwowanie pamięci dla całej tablicy.
WaŜne jest więc aby rozmiar i typ elementów ustalać dokładnie do potrzeb ( nie na wyrost).
C/C++ - ZłoŜone typy danych, wskaźniki Struktury
Struktury są zbiorami elementów (zmiennych) róŜnych typów. Struktura jest złoŜonym typem danych definiowanym przez uŜytkownika. Elementy struktury są nazywane takŜe polami. Po zdefiniowaniu struktury moŜliwe jest deklarowanie zmiennych tego typu.
słowo
Przykład struktury
nazwa struktury
kluczowe
struct osoba {
char nazwisko[20];
char imie[15];
pola
char adres[30];
int
wiek;
};
średnik kończący deklarację struktury
C/C++ - ZłoŜone typy danych, wskaźniki Struktury c.d.
Zmienne typu strukturalnego moŜna deklarować na dwa sposoby. Pierwszy to wymienienie nazw deklarowanych zmiennych za nawiasem klamrowym zamykającym listę pól, a przed średnikiem kończącym deklarację struktury. W tym przypadku moŜna opuścić nazwę struktury.
struct osoba {
char nazwisko[20];
char imie[15];
char adres[30];
int
wiek;
}a,b,c;
zmienne typu osoba
Drugi sposób to deklaracja postaci:
struct osoba d,e,f;
C/C++ - ZłoŜone typy danych, wskaźniki Struktury c.d.
Dostęp do pola zmiennej typu strukturalnego realizowany jest z wykorzystaniem operatora . (kropki) postaci:
nazwa_zmiennej. nazwa_pola
Przykład
struct { // struktura bez nazwy
int x;
int y;
} a,b;
a.x=7;
a.y=5;
b=a;
//skopiowanie wszystkich pól
Ostatnia instrukcja przypisania pokazuje, Ŝe moŜna przypisać wszystkie pola jednej zmiennej innej zmiennej w jednej instrukcji przypisania.
C/C++ - ZłoŜone typy danych, wskaźniki Struktury c.d.
Z uwagi na to, iŜ struktury są wykorzystywane do opisu obiektów, często są wykorzystywane jako typ elementów tablic.
Deklaracja wektora elementów typu osoba (strukturalnego): struct osoba {
char nazwisko[20];
char imie[15];
char adres[30];
int
wiek;
};
struct osoba lista_osob[30];
lista_osob[6].wiek=21;
lista_osob[29].wiek=lista_osob[6].wiek;
W przedostatnim wierszu następuje przypisanie wartości polu wiek siódmego elementu tablicy, a w ostatnim przypisanie wartości pola wiek siódmego elementu tablicy polu wiek elementu ostatniego.
C/C++ - ZłoŜone typy danych, wskaźniki Struktury - pola bitowe
W języku C istnieje moŜliwość deklarowania struktury której elementy (pola) zajmują określoną liczbę bitów. Takie rozwiązanie umoŜliwia dostęp do pojedynczych bitów. Postać deklaracji struktury pól bitowych jest następująca: nazwa pola bitowego
długość pola w
struct poleb {
bitach
unsigned b1:1;
unsigned b2:1;
elementy
int b3:2;
};
nazwa elementu pola
Dostęp do elementów pola bitowego
struct poleb pb;
pb.b1=0;
Elementy pola bitowego mogą być typu int, signed, unsigned. Jeśli element ma długość 1 bitu to musi być typu unsigned. Długość pola bitowego w systemach 32-bitowych wynosi 32 bity (w 16-bitowych –16 bitów).
C/C++ - ZłoŜone typy danych, wskaźniki Unie
Unia umoŜliwia deklarację zmiennych róŜnych typów współdzielących miejsce w pamięci. Oznacza to, Ŝe ta sama komórka pamięci jest wykorzystywana przez róŜne zmienne zadeklarowane w unii.
Deklaracja unii, jak równieŜ dostęp do elementów unii jest analogiczny do struktury. RóŜnica w deklaracji polega na tym, Ŝe w tym przypadku wykorzystuje się słowo kluczowe union.
Przykład deklaracji
union alfa {
int i;
char c;
}u1,u2;
union alfa u3,u4;
Unia zajmuje tyle miejsca w pamięci ile zajmuje jej najdłuŜsze pole.
C/C++ - ZłoŜone typy danych, wskaźniki Unie c.d.
Dostęp do elementów unii
union alfa {
unsigned int i;
unsigned char c[4];
}u1;
u1.i=25;
cout<<(int)u1.c[0]<<'\n';
cout<<(int)u1.c[1]<<'\n';
cout<<(int)u1.c[2]<<'\n';
cout<<(int)u1.c[3]<<'\n';
PowyŜsza postać unii umoŜliwia dostęp do poszczególnych bajtów liczby całkowitej poprzez tablicę czterech znaków.
przykład
C/C++ - ZłoŜone typy danych, wskaźniki Unie + pola bitowe
struct poleb {
unsigned b0:1;
unsigned b1:1;
W przykładzie mamy pokazany
unsigned b2:1;
sposób dostępu do jednego bajtu jako
unsigned b3:1;
elementu typu unsigned char oraz
unsigned b4:1;
do ka
unsigned b5:1;
Ŝdego z jego bitów z osobna
unsigned b6:1;
(dzięki wykorzystaniu pola
unsigned b7:1;
bitowego).
};
union alfa{
struct poleb p;
unsigned char l;
}u;
u.l=10;
cout<<u.p.b7<<u.p.b6<<u.p.b5<<u.p.b4<<u.p.b3<<u.p.b2<<u.p.b1<<u.p.b0; przykład polabitowe.cpp
C/C++ - ZłoŜone typy danych, wskaźniki Wyliczenia
Typ wyliczeniowy jest zbiorem symbolicznych stałych całkowitych określających wszystkie dopuszczalne wartości jakie moŜe przyjmować zmienna tego typu.
Typ wyliczeniowy definiuje się za pomocą słowa kluczowego enum.
Przykład wyliczenia
enum nazwa_samochodu{audi,bmw,fiat,ford,toyota}bryka;
słowo
nazwa typu
zbiór dopuszczalnych
deklaracja
kluczowe
stałych symbolicznych
zmiennej
PowyŜsza deklaracja spowoduje, Ŝe stałym ze zbioru wartości typu nazwa_samochodu przypisane zostaną kolejne liczby całkowite od 0 do 4. Wartości przypisywane stałym moŜna ustalać na etapie definiowania typu. Na przykład definicja postaci:
enum miara{sztuka=1,tuzin=12,kopa=60,gros=144};
spowoduje nadanie wartości określonych w liście wyliczenia.
C/C++ - ZłoŜone typy danych, wskaźniki Wyliczenia - operacje na elementach typu wyliczeniowego
enum nazwa_samochodu{audi,bmw,fiat,ford,toyota};
enum nazwa_samochodu bryka;
bryka=toyota;
cout<<bryka;
// wyświetlenie na konsoli
W powyŜszym przykładzie został zdefiniowany typ wyliczeniowy nazwa_samochodu, zadeklarowana został zmienna bryka tego typu.
Następnie nadano jej wartość toyota. W sytuacji kiedy chcemy wyświetlić wartość zmiennej auto na ekran zostanie wyprowadzona wartość liczbowa tej stałej, czyli 4. Wartości symbolicznych moŜna uŜywać bez ograniczeń w wyraŜeniach oraz blokach warunków. przykład wyliczenia.cpp
C/C++ - ZłoŜone typy danych, wskaźniki Wskaźniki
Wskaźnik jest zmienną słuŜącą do przechowywania adresu (zmiennej dowolnego typu, funkcji, kolejnego wskaźnika).
Wskaźnik wskazuje adres zmiennej typu określonego na etapie deklaracji.
Wskaźniki w języku C mają następujące zastosowania:
•
umoŜliwiają funkcjom modyfikację przekazywanych parametrów,
•
umoŜliwiają korzystanie z mechanizmu dynamicznego przydziału pamięci,
•
umoŜliwiają korzystanie z dynamicznych struktur danych.
Omówione zostaną wskaźniki do zmiennych, tablic, struktur,wskaźników.
C/C++ - ZłoŜone typy danych, wskaźniki Wskaźnik do zmiennej
Postać przykładowej deklaracji wskaźnika jest następująca: int *wsk_do_int;
typ wskazywanego
elementu (bazowy)
nazwa zmiennej
gwiazdka
wskaźnikowej
Zadeklarowana zmienna wskaźnikowa moŜe przechowywać adresy zmiennych typu int, mówimy, Ŝe wskazuje na element typu int.
Oprócz operatora gwiazdki *, w operacjach wskaźnikowych wykorzystywany jest operator & . SłuŜy on do uzyskiwania adresu zmiennej.
Zapis &x oznacza adres w pamięci zmiennej x.
Operator * poza deklaracją wskaźnika słuŜy do określenia wartości znajdującej pod adresem, który wskaźnik wskazuje. Instrukcja x=*wsk_do_int ;
realizuje przypisanie zmiennej x wartości spod adresu wsk_do_int.
C/C++ - ZłoŜone typy danych, wskaźniki
Wskaźnik do zmiennej
Przykład
int i,j,*wsk_do_int;
i=3;
wsk_do_int=&i;
j=*wsk_do_int;
cout<<i<<'\n'<<j<<'\n'<<wsk_do_int<<'\n';
C/C++ - ZłoŜone typy danych, wskaźniki
Wskaźnik do zmiennej – arytmetyka wskaźnikowa
Na wskaźnikach moŜna wykonywać operacje dodawania i odejmowania oraz moŜna je porównywać.
Operacja dodania do wskaźnika wartości jeden oznacza, Ŝe będzie on teraz wskazywać nie na kolejny bajt w pamięci lecz na adres większy o tyle, ile bajtów zajmuje typ bazowy wskaźnika.
Przykład
int i,*wsk_do_int;
wsk_do_int=&i;
cout<<wsk_do_int<<'\n';
cout<<wsk_do_int+1<<'\n';
C/C++ - ZłoŜone typy danych, wskaźniki Wskaźnik do tablicy
Wskaźnik do tablicy zawiera adres pierwszego jej elementu.
Deklaracja wskaźnika do tablicy jest taka sama jak do zmiennej. Aby wskaźnik mógł być wykorzystany do operacji na tablicy, typ bazowy wskaźnika musi być taki sam jak typ elementów w tablicy.
RóŜnica w operowaniu na tablicach w porównaniu ze zmiennymi innych typów polega na dodatkowej moŜliwości uzyskiwaniu adresu pierwszego elementu tablicy jednowymiarowej.
W celu uzyskania adresu pierwszego elementu tablicy moŜna skorzystać z operatora & w następujący sposób:
wsk=&tab[0];
lub w sposób charakterystyczny dla tablic:
wsk=tab;
tzn. podając nazwę tablicy.
C/C++ - ZłoŜone typy danych, wskaźniki
Wskaźnik do tablicy c.d.
Odwołanie do elementu tablicy z wykorzystaniem wskaźnika zostało przedstawione w sekwencji poniŜej.
char lan[3]={'a','b','c'},*wsk;
wsk=lan;
cout<<*(wsk+2);
Wyświetlony zostanie ostatni element tablicy znaków.
W języku C moŜliwe jest zapamiętanie stałej w postaci łańcucha znaków z wykorzystaniem wskaźnika do typu char:
char *str="werw3erwerwerwerwe”;
cout<<str;
przykład
C/C++ - ZłoŜone typy danych, wskaźniki
Wskaźnik do tablicy c.d.
W przypadku kiedy odwołujemy się przy pomocy wskaźnika do tablicy dwuwymiarowej to konieczne jest ustalenie połoŜenia elementu tablicy w pamięci w stosunku do elementu [0][0] (przesunięcia).
const int N=5,M=6;
int i,j,tab[N][M],*wsk;
wsk=&tab[0][0]; //pozyskanie adresu tablicy
i=4,j=5;
tab[i][j]=44;
cout<<*(wsk+(i*M+j));//wyświetlenie elementu i,j
przykład
C/C++ - ZłoŜone typy danych, wskaźniki Wskaźnik do struktury
Wskaźniki do struktur deklarujemy tak jak wskaźniki do typu prostego. RównieŜ sposób uzyskania adresu zmiennej typu strukturalnego jest standardowy z wykorzystaniem operatora pobierania adresu &.
Sposób dostępu do pola struktury odbywa się z wykorzystaniem operatora ->
C/C++ - ZłoŜone typy danych, wskaźniki
Wskaźnik do struktury c.d.
Przykład 1
struct osoba {
char nazwisko[20];
char imie[15];
int
wiek;
};
struct osoba a,*wsk;
wsk=&a; // pobranie adresu zmiennej a
wsk->wiek=21;// ustawienie pola wiek poprzez wskaźnik cout<<a.wiek;
przyklad
C/C++ - ZłoŜone typy danych, wskaźniki
Wskaźnik do struktury c.d.
Przykład 2
Dostęp do pola struktury będącego elementem tablicy przez wskaźnik.
struct osoba {
char nazwisko[20];
char imie[15];
int
wiek;
}klasa[30];
struct osoba *wsk;
wsk=klasa;
(wsk+10)->wiek=21;
cout<<klasa[10].wiek;
przykład
C/C++ - ZłoŜone typy danych, wskaźniki Wskaźniki do wskaźników
W C++ moŜliwe jest korzystanie z wielokrotnego adresowania pośredniego.
MoŜliwa jest zatem sytuacja, Ŝe wskaźnik zawiera nie adres zmiennej lecz adres komórki pamięci w której znajduje się adres docelowego elementu.
int x,*p,**q; // deklaracja zmiennej, wskaźnika i wskaźnika do
// wskaźnika
x=123;
p=&x; // pobranie adresu zmiennej
q=&p; // pobranie adresu wskaźnika
cout<<x; //wyświetlenie zmiennej x
cout<<*p; // wyświetlenie zawartości spod adresu zmiennej x cout<<**q; // wyświetlenie zawartości spod adresu przechowywanego
//w komórce wskazywanej przez wskaźnik q
przykład
C/C++ - ZłoŜone typy danych, wskaźniki Wskaźniki – typowe błędy
1. UŜycie niezainicjalizowanego wskaźnika do zapisu
int x,*p;
x=123;
*p=x; // zapis pod nieokreślony adres
2. Przypisanie wskaźnikowi wartości zmiennej (int, char) zamiast jej adresu int x,*p;
x=123;
p=x;
cout<<*p // wyprowadzenie wartości spod
//adresu 123
przykład