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