440 likes | 558 Views
Podstawy informatyki 2013/2014. Łukasz Sztangret Katedra Informatyki Stosowanej i Modelowania Prezentacja przygotowana w oparciu o materiały Danuty Szeligi i Pawła Jerzego Matuszyka. Tablice.
E N D
Podstawy informatyki2013/2014 Łukasz SztangretKatedra Informatyki Stosowanej i Modelowania Prezentacja przygotowana w oparciu o materiałyDanuty Szeligi i Pawła Jerzego Matuszyka
Tablice • Tablica – agregat składający się z określonej liczby obiektów tego samego typu zajmujący w pamięci ciągły obszar • Typy tablicowe są typami pochodnymi od dostępnych typów danych • Typ tablicowy pochodzący od typu T oznaczany jest jako T[]
Tablice Postać definicji tablicy: typ_elementunazwa_tablicy[liczba_elementów]; • Typ elementu: • jeden z typów fundamentalnych (z wyjątkiem void) • typ wyliczeniowy (enum) • wskaźnik • inna tablica • typ zdefiniowany przez użytkownika • wskaźnik do pokazywania na składniki klasy • Liczba elementów • rozmiar tablicy musi być wyrażeniem stałym (lub stałą), o wartości znanej w trakcie kompilacji • Nazwa tablicy jest jednocześnie adresem jej zerowego elementu!
Definiowanie tablic - przykłady int a[4]; - 4-ro elementowa tablica elementów typy int constint n=10; char tab[n]; int n=10; char tab[n]; Niektóre kompilatory dopuszczają powyższą formę, ale nie jest ona zgodna ze standardem języka (taki zapis nie „kompiluje się” na kartce). a a[0] a[1] a[2] OK a[3] BŁĄD
Definiowanie tablic Próba oszukania kompilatora int a; cin>>a; constintn=a; inttab[n]; się nie uda. Będzie błąd. Stała, ale nie jest znana na etapie kompilacji
Odwołania do elementów tablicy • Do elementów tablicy można się odwołać korzystając z operatora indeksowania [] • Elementy tablicy są numerowane kolejnymi liczbami naturalnymi, począwszy od zera • Numery elementów w tablicy nazywane są indeksami. Tablica n-elementowa ma elementy o indeksach od 0 do n-1 constint n=5; inttab[n]; tab[0]=1; tab[1]=tab[0]; tab[2]=4; tab[3]=tab[4]=7; tab[5]=10; Element o indeksie 5 nie istnieje
Inicjalizacja tablic • Tablice podobnie jak inne zmienne można inicjalizować podczas definiowania • Inicjalizacja tablicy – nadanie wartości elementom tablicy w momencie jej definicji. int t[4]={1,2,3,4}; jest równoważne z int t[4]; t[0]=1; t[1]=2; t[2]=3; t[3]=4; • Tablice można zainicjalizować częściowo: int t[4]={1,2}; jest równoważne z int t[4]={1,2,0,0};
Inicjalizacja tablic • Lista inicjalizacyjna tablicy może zawierać stałe oraz zmienne: int n1 = 0, n2 = 1, n3 = 2; int t[4] = {n1, n2, n3, 5};
Inicjalizacja tablic • Możliwe jest zdefiniowanie tablicy bez jawnego podawania jej rozmiaru – wymaga to jednak jednoczesnej inicjalizacji tablicy. • Kompilator na podstawie liczby elementów podanych na liście inicjalizacyjnej rezerwuje odpowiednią ilość miejsca pamięci: int t[]={1,2,3,4};
Odwołania do elementów tablicy • Najczęstszą konstrukcją języka używaną do przetwarzania tablic jest pętla for const int n = 4; int t[n]; for(int i = 0; i < n; i++) t[i] = i+2; for(int i = 0; i <= n - 1; i++) t[i] = i+2;
Wypisywanie tablic intmain() { constint n=4; inttab[n]; for (int i=0; i<n; i++) tab[i]=i+2; cout<<tab<<endl; for (int i=0; i<n; i++) cout<<tab[i]<<endl; return 0; } 0027FD40 2 3 4 5
Stałe tablice • Stała tablica – tablica zawierająca stałe elementy. • Tablica obiektów stałych musi podczas definicji zostać zainicjalizowana co najmniej jednym elementem lub pustą listą inicjalizacyjną. • Elementy niezainicjalizowane są inicjalizowane zerami odpowiedniego typu.
Stałe tablice constint a = 7; int b = 7; const int t1[3] = {2, 4, a}; constint t2[3] = {2, 4, b}; constint t3[3] = {2}; //równoważne {2,0,0} constint t4[3] = {}; //równoważne {0,0,0} constint t5[3]; BŁĄD t1[1] = 0; BŁĄD
Tablice znakowe • Tablice ciągów znaków reprezentujące napis – na końcu zawsze dodawany jest znak o kodzie 0, NULL: '\0‘ char tekst[20]={"przyklad"}; Znak NULL jest automatycznie dodawany do końca ciągu, ponieważ ciąg "przyklad" ujęto w cudzysłów.
Tablice znakowe intmain() { char tab1[]={"AGH"}; char tab2[]={"-----"}; char tab3[]={"-----"}; cout<<tab1<<endl<<tab2<<endl<<tab3<<endl; int i=0; while (tab1[i]) { tab2[i]=tab1[i]; i++; } cout<<tab2<<endl; cout<<i<<endl; i=-1; while (i++,tab3[i]=tab1[i]); cout<<tab3<<endl; cout<<i<<endl; for (i=0; i<5; i++) cout<<tab3[i]; cout<<endl; return 0; } AGH ----- ----- AGH-- 3 AGH 3 AGH -
Tablice znakowe • Tablice o elementach typu znakowego nie muszą być traktowane jako napisy: char tekst[20] = {'p','r','z','y','k','l','a','d'}; • tu jest znak Null ponieważ reszta tablicy jest wypełniana zerami char tekst[] = {'p','r','z','y','k','l','a','d'}; • tu znaku Null nie będzie ponieważ zostanie stworzona tablica 8 elementowa
Tablice wielowymiarowe • Postać definicji tablicy: typ_elementunazwa_tablicy[l1][l2]...[ln]; • Liczba elementów tablicy równa jest l1*l2*...*ln • Liczba wymiarów tablicy odpowiada liczbie użytych par nawiasów kwadratowych [] w definicji, np.: // tablica dwuwymiarowa, łącznie 16 elementów double tab2D[2][8]; // tablica czterowymiarowa, łącznie 400 elementów unsignedint tab4D[2][2][5][20];
Tablice wielowymiarowe • Deklaracja wymiarów umożliwia obliczenie położenia elementów tablicy w kolejnych wymiarów, np.: int a[2][4]; Element a[i][j] leży w stosunku do początku tablicy o (i*4)+j elementów od początku tablicy • Statycznie zdefiniowana tablica wielowymiarowa może być traktowana jako ciągły obszar pamięci a a[0][0] a[0][1] a[0][2] a[0][3] a[1][0] a[1][1] a[1][2] a[1][3] a[1][2] leży 1*4+2 elementów od początku tablicy
Przekazywanie tablic do funkcji • Argumentem formalnym funkcji może być typ tablicowy. • Jako argument aktualny przesyłany jest tylko adres początku tablicy. • Rozmiar tablicy można przekazać tylko jawnie jako inny parametr funkcji. // deklaracja funkcji int fx1(double tab[]); int fx2(double tab[], unsignedintsize); // definicja tablicy double tablica[4]; // wywołania funkcji: fx1(tablica); fx2(tablica, 4);
Przekazywanie tablic do funkcji int fx1(double tab[]); double tablica[4]; W wywołaniu funkcji przyjmującej tablicę podajemy tylko nazwę tablicy! fx1(tablica); fx1(tablica[]); fx1(tablica[4]); OK Błąd kompilacji Próba wysłania do funkcji elementu tablicy o indeksie 4 (JEDNEJ LICZBY)
Przekazywanie tablic do funkcji void f(double t[]) { cout<<sizeof(t)<<endl; } intmain() { double t[5]; cout<<sizeof(t)<<endl; f(t); return 0; } 4 40
Przekazywanie tablic do funkcji • Przekazanie do funkcji tablicy n–wymiarowej wymaga zadeklarowania co najmniej n-1 ostatnich wymiarów void fx3D(floattab[][4][3]); void fx3D(floattab[][][3]); BŁĄD • Podane wymiary muszą być wyrażeniami stałymi, znanymi na etapie kompilacji • Zadeklarowane wymiary są używane przez kompilator w celu: • sprawdzenia zgodności typu tablicowego z argumentem aktualnym – przy wywołaniu funkcji • obliczenia położenia kolejnych wierszy najwyższego wymiaru tablicy i wierszy następnych wymiarów – wewnątrz ciała funkcji
Przekazywanie tablic do funkcji constint a = 4, b = 3; void fx3D(inttab[][a][b], int n) { int l = 0; for (int i = 0; i < n; i++) for (int j = 0; j < a; j++) for (int k = 0; k < b; k++) tab[i][j][k] = l++; } intmain() { int t1[5][4][3]; int t2[5][3][4]; fx3D(t1,5); fx3D(t2,5); } BŁĄD
#include<iostream> using namespace std; void podwoj(int a[][2]); void wypisz(int a[][2]); int main() { int tab[][2]={1,2,3,4}; wypisz(tab); podwoj(tab); wypisz(tab); return 0; } void podwoj(int a[][2]) { for (int i=0; i<2; i++) { for (int j=0; j<2; j++) a[i][j]*=2; } wypisz(a); } void wypisz(int a[][2]) { for (int i=0; i<2; i++) { for (int j=0; j<2; j++) cout << a[i][j] << "\t"; cout << endl; } cout << endl; } Przekazywanie tablic do funkcji 2 4 6 8 2 3 4 2 4 6 8
Wskaźniki • Dla typu T zapis T* oznacza wskaźnik do T, czyli zmienna typu T* może przechowywać adres (w pamięci) obiektu typu T. • Podstawową operacją na wskaźniku jest wyłuskanie, czyli odwołanie się do obiektu pokazywanego przez ten wskaźnik. • Operatorem adresowania pośredniego jest przedrostkowy jednoargumentowy operator (wyłuskania) *. • Operatorem dualnym do operatora wyłuskania jest przedrostkowy jednoargumentowy operator pobrania adresu & (zwraca adres obiektu).
Wskaźniki int a = 1; int* p = &a; cout << a; cout << *p; cout << p; cout << &a; 1 1 0032FEA0 0032FEA0
Wskaźniki • Z definicji wskaźnika wynika, że wskaźnik pokazuje na obiekt. • Referencja nie jest obiektem nie można definiować wskaźnika do referencji. • Obiekt rejestrowy nie ma adresu nie można definiować wskaźnika do obiektu rejestrowego. • Typ wskaźnika precyzyjnie określa, na jakie obiekty można takim wskaźnikiem pokazywać. • Generalnie, wskaźnikiem typu A* nie można pokazywać na obiekty typu B.
Wskaźniki • Definicja wskaźnika tworzy jedynie obiekt wskaźnikowy – wskaźnik nie pokazuje na konkretny obiekt: int* p; // p pokazuje na "nie-wiadomo-co" • Aby bezpiecznie używać wskaźnika, należy go ustawić: p = &n; // teraz p pokazuje na n • Najbezpieczniej ustawić wskaźnik od razu (w definicji): int* p = 0; // p pokazuje na adres 0x00000000 • Żaden obiekt nie może być umieszczony w pamięci pod adresem 0. • Zero pełni rolę literału wskaźnikowego, oznaczającego wskaźnik, który nie pokazuje na żaden obiekt.
Tablice a wskaźniki • Tablica (lub jej początek) jest obiektem w pamięci, ma więc zatem swój własny adres. • Nazwa tablicy tab typu T[] jest adresem jej pierwszego elementu &tab[0] • Tablica tab typu T[] może być niejawnie skonwertowana do typu T*; Rezultatem jest wskaźnik do pierwszego elementu tablicy tab.
Tablice a wskaźniki • Jeżeli mamy wskaźnik T* p, to operatory: * oraz [] działają identycznie: *p == p[0] *(p + i) == p[i] Przemienność dodawania: a+b=b+a *(p+i)==*(i+p) p[0]==0[p]
Tablice a wskaźniki int t[] = {1, 4, -2}; // zmieniamy trzeci element tablicy *(t+2) = 5; // wynik: {1, 4, 5} int * p; // równoważnie można zapisać: p = &t[0]; // *p wynosi 1 p = t; // *p wynosi 1 // p wskazuje na ostatni element p = &t[2]; // *p wynosi 5 p = t+2; // *p wynosi 5
Tablice a wskaźniki • Jeżeli wskaźnik pokazuje na element tablicy można użyć go zgodnie z notacją typową dla tablic: double tab[5] = {-2,-1,0,1,2}; double * ptr; ptr = &tab[0]; // lub: ptr = tab; for (int i = 0; i < 5; i++) cout << ptr[i] << ' ' << tab[i] << endl;
Tablice a wskaźniki • Wskaźnik taki można przesuwać po elementach tablicy: ptr++; for (int i = 0; i < 4; i++) cout << ptr[i] << ' ' << tab[i] << endl; //tab++;BŁĄD
Tablice a wskaźniki • Tablica obiektów typu int: int a[10]; // a typu: int [10] int* pn = &a[3]; // pn typu: int* *pn = 7; // a[3] = 7 int* pa = a; // pa typu: int* // nastąpiła konwersja: int[] int* pa[3] = 7; // a[3] = 7 • Tablica obiektów typu int* (wskaźników): int n; // n typu: int int* ap[10]; // ap typu: int *[10] ap[3] = &n; // ustawiamy adres n *ap[3] = 7; // n = 7
Wskaźniki w argumentach funkcji #include<iostream> usingnamespacestd; voidpodwoj(int *wsk); intmain() { int a=2; cout << a << endl; podwoj(&a); cout << a << endl; return 0; } voidpodwoj(int *wsk) { *wsk*=2; } 2 4
int, int*, int** int a=1; int *w=&a; int **ww=&w; cout<<&ww<<endl; cout<<ww<<endl; cout<<*ww<<endl; cout<<**ww<<endl; 0043F8CC 0043F8CF 1 a 0043F8D0 0043F8D3 0043F8CC w 0043F8D4 0043F8D7 0043F8D0 ww 0043F8D4 0043F8D0 0043F8CC 1
void f1(int), void f3(int*), void f5(int**)void f2(int&), void f4(int*&), void f6(int**&) void f1(intfa){} intmain() { int a=1; int *w=&a; int **ww=&w; f1(a); return 0; } 0043F8CC 0043F8CF 1 a Nie możemy zmienić: a, w, ww Możemy zmienić: 0043F8D0 0043F8D3 0043F8CC w 0043F8D4 0043F8D7 0043F8D0 ww 0043F8D8 0043F8DB 1 fa
void f1(int), void f3(int*), void f5(int**)void f2(int&), void f4(int*&), void f6(int**&) void f2(int &fa){} intmain() { int a=1; int *w=&a; int **ww=&w; f2(a); return 0; } 0043F8CC 0043F8CF 1 a fa Nie możemy zmienić: w, ww Możemy zmienić: a 0043F8D0 0043F8D3 0043F8CC w 0043F8D4 0043F8D7 0043F8D0 ww
void f1(int), void f3(int*), void f5(int**)void f2(int&), void f4(int*&), void f6(int**&) void f3(int *fw){} intmain() { int a=1; int *w=&a; int **ww=&w; f3(w); return 0; } 0043F8CC 0043F8CF 1 a Nie możemy zmienić: w, ww Możemy zmienić: a 0043F8D0 0043F8D3 0043F8CC w 0043F8D4 0043F8D7 0043F8D0 ww 0043F8D8 0043F8DB 0043f8CC fw
void f1(int), void f3(int*), void f5(int**)void f2(int&), void f4(int*&), void f6(int**&) void f4(int *&fw){} intmain() { int a=1; int *w=&a; int **ww=&w; f4(w); return 0; } 0043F8CC 0043F8CF 1 a Nie możemy zmienić: ww Możemy zmienić: a, w 0043F8D0 0043F8D3 0043F8CC w fw 0043F8D4 0043F8D7 0043F8D0 ww
void f(int), void f(int*), void f(int**)void f(int&), void f(int*&), void f(int**&) void f5(int **fww){} intmain() { int a=1; int *w=&a; int **ww=&w; f5(ww); return 0; } 0043F8CC 0043F8CF 1 a Nie możemy zmienić: ww Możemy zmienić: a, w 0043F8D0 0043F8D3 0043F8CC w 0043F8D4 0043F8D7 0043F8D0 ww 0043F8D8 0043F8DB 0043F8D0 fww
void f(int), void f(int*), void f(int**)void f(int&), void f(int*&), void f(int**&) void f6(int **&fww){} intmain() { int a=1; int *w=&a; int **ww=&w; f6(ww); return 0; } 0043F8CC 0043F8CF 1 a Nie możemy zmienić: Możemy zmienić: a, w, ww 0043F8D0 0043F8D3 0043F8CC w 0043F8D4 0043F8D7 0043F8D0 ww fww
Możliwe wywołania funkcji int a=1; int *w=&a; int **ww=&w; 0043F8CC 0043F8CF 1 a 0043F8D0 0043F8D3 0043F8CC w 0043F8D4 0043F8D7 0043F8D0 ww int a; int *w; int **ww; void f1(int); f1(a); f1(*w); f1(**ww); void f2(int&); f2(a); f2(*w); f2(**ww); void f3(int*); f3(&a); f3(w); f3(*ww); void f4(int*&); -- f4(w); f4(*ww); void f5(int**); -- f5(&w); f5(ww); void f6(int**&); -- -- f6(ww);
Prezentacja udostępniona na licencji CreativeCommons: Uznanie autorstwa, Na tych samych warunkach 3.0. Pewne prawa zastrzeżone na rzecz autorów. Zezwala się na dowolne wykorzystywanie treści pod warunkiem wskazania autorów jako właścicieli praw do prezentacji oraz zachowania niniejszej informacji licencyjnej tak długo, jak tylko na utwory zależne będzie udzielana taka sama licencja. Tekst licencji dostępny jest na stronie: http://creativecommons.org/licenses/by-sa/3.0/deed.pl