320 likes | 372 Views
Tablice. Tablica jest to zbiór elementów tego samego typu, które zajmują ciągły obszar w pamięci. Tablice są typem pochodnym , tzn. buduje się je z elementów jakiegoś typu nazywanego typem składowym . Przykład :. int A[50]; float Tab[20]; unsigned long int W[30]; char Tekst[80]; .
E N D
Tablica jest to zbiór elementów tego samego typu, które zajmują ciągły obszar w pamięci. • Tablice są typempochodnym, tzn. buduje się je z elementów jakiegoś typu nazywanego typemskładowym. Przykład: • int A[50]; • float Tab[20]; • unsigned long int W[30]; • char Tekst[80];
Rozmiar tablicymusi być stałą, znaną już w trakcie kompilacji; • Kompilator musi wiedzieć ile miejsca ma zarezerwować na daną tablicę. • Rozmiar ten nie może być ustalany dopiero w trakcie pracy programu. Przykład: • cout <<Podaj rozmiar tablicy: ; • int n; • cin >> n; • int A[n]; // błąd!!!
Typ składowy tablic: • typ fundamentalny (z wyjątkiem void); • typ wyliczeniowy (enum); • inna tablica; • wskaźniki; • obiekty typu zdefiniowanego przez użytkownika (czyli klasy); • wskaźniki do pokazywania na składniki klasy.
Elementy tablicy: • int A[5]; // 5 elementów typuint • A[0] A[1] A[2] A[3] A[4] • Numeracja elementów tablicy zaczyna się od zera. • Element A[5] nie istnieje. • Próba wpisania jakiejś wartości do A[5] nie będzie sygnalizowana jako błąd. • W języku C++ zakres tablic nie jest sprawdzany. • Wpisanie wartości do nieistniejącego elementu A[5] spowoduje zniszczenie w obszarze pamięci wartości, wpisanej bezpośrednio za tablicą.
Przykład: • int A[5]; • int x = 20; • Próba zapisu: • A[5] = 100; spowoduje zniszczenie wartości zmiennej x, która została umieszczona w pamięci bezpośrednio za tablicą A.
Inicjalizacja tablic: Tablicę można zainicjować w momencie definicji tablicy. Przykład: • int A[5] = { 21, 4, 45, 38, 17 }; Wynik zainicjowania tablicy A: • A[0] = 21 • A[1] = 4 • A[2] = 45 • A[3] = 38 • A[4] = 17
Inicjowanie tablic: • Jeżeli w momencie inicjowania na liście jest więcejelementów, niż wynika z definicji to kompilator zasygnalizuje błąd. • Podczas inicjowania kompilator sprawdza, czy nie jest przekroczony rozmiar tablicy. Możliwa jest taka inicjalizacja tablicy: • int A[5] = {21, 4}; • A[0] = 21 • A[1] = 4 • A[2] = 0 • A[3] = 0 • A[4] = 0
Inicjowanie tablic: Kolejny sposób inicjowania tablicy: • int A[ ] = { 21, 4, 45, 38, 17 }; • Kompilator w tym przypadku przelicza, ile liczb podano w klamrach. • W efekcie rezerwowana jest pamięć na te elementy.
Przekazywanie tablicy do funkcji: • Tablice w C++nie są przesyłane do funkcji przez wartość. • Przez wartość można przesyłać tylko pojedyncze elementy tablicy, ale nie całość. • Tablice przesyła się podając do funkcji tylko adres początku tablicy. Przykład: • float X[ ] = { 21, 4, 45, 38, 17 }; • void Sort ( float X[ ] ); Funkcję Sort wywołujemy w sposób następujący: • Sort ( X );
W języku C++nazwatablicyjest jednocześnie adresem elementu zerowego. • Ponadto wyrażenie: • X + 3 • jest adresem tego miejsca w pamięci, gdzie znajduje się element o indeksie 3, czyli X[3]. • W naszym przykładzie jest to element o wartości 38. Adres takiego elementu to również: • &X [3] • Znak & jest jednoargumentowym operatorem oznaczającym uzyskiwanie adresu danego obiektu. • Zatem poniższe dwa wyrażenia są równoważne: • X + 3&X [3]
Tablice znakowe Specjalnym rodzajem tablic są tablice do przechowywania znaków. Przykład: char tekst [80]; • W pewnych tablicach znakowych po ciągu znaków następuje znak o kodzie 0 ( znak NULL). • Znak ten stosuje się do oznaczenia końca ciągu znaków innych niż NULL. • Ciąg znaków zakończony znakiem NULL nazywamy łańcuchem.
0 1 2 3 4 … … 77 78 79 C + + NULL Inicjowanie tablic znakowych: Tablicę tekst można zainicjalizować w trakcie definicji : char tekst [80] = { C++ }; • nie wymienione elementy inicjalizuje się do końca tablicy zerami; • znak NULL został automatycznie dopisany po ostatnim znaku + dzięki temu, że inicjowaliśmy tablicę ciągiem znaków ograniczonym cudzysłowem.
Inicjowanie tablic znakowych: Jest też inny sposób inicjalizacji tablicy znaków: • char tekst [80] = { ‘C’, ‘+’, ‘+’ }; • Zapis taki jest równoważny wykonaniu trzech instrukcji: • tekst [0] = ‘C’; • tekst [1] = ‘+’; • tekst [2] = ‘+’; • Ponadto, ponieważ nie było tu cudzysłowu, kompilator nie dokończył inicjowania znakiem NULL. • Wszystkie elementy tablicy poczynając od tekst [3] do tekst [79] włącznie zostaną zainicjowane zerami. • Ponieważ znak NULL ma kod 0 - zatem łańcuch w tablicy tekst zostanie poprawnie zakończony.
Pułapka ! ! !: • char tekst [ ] = { ‘C’, ‘+’, ‘+’ }; • Jest to definicja tablicy znakowej o 3 elementach, w której znajdą się znaki ‘C’, ‘+’ i ‘+’. • Znaku NULL tam nie będzie. • Wniosek - tablica tekst nie przechowuje łańcucha znaków, lecz pojedyncze znaki. W definicji: • char tekst [ ] = { C++ }; • zostanie zarezerwowana pamięć dla 4 elementów tablicy znakowej tekst. • kolejne elementy tablicy przechowują następujące znaki: ‘C’, ‘+’, ‘+’ i NULL.
Przykład: rozmiar tablicy pierwszej:10 #include <iostream.h> #include <conio.h> void main () { char napis1[ ] = { "Nocny lot" }; char napis2[ ] = { 'N', 'o', 'c', 'n', 'y', ' ', 'l', 'o', 't' }; clrscr (); cout << "rozmiar tablicy pierwszej: " << sizeof(napis1) <<endl; cout <<"rozmiar tablicy drugiej: " << sizeof(napis2) <<endl; } rozmiar tablicy drugiej:9
Wpisywanie łańcuchów do tablic: • tekst [80] = Nocny lot; // błąd • tekst = Nocny lot; // błąd Oto przykład funkcji kopiującej łańcuchy: void strcopy ( char cel [ ], char zrodlo [ ] ) { for (int i = 0; ; i++ ) { cel [i] = zrodlo [i]; if (cel [i] == NULL ) break; } }
Wpisywanie łańcuchów do tablic: Oto inny sposób wykonania kopiowania łańcuchów: void strcopy ( char cel [ ], char zrodlo [ ] ) { int i = 0; do cel [i] = zrodlo [i]; // kopiowanie while ( cel [i++] != NULL ); // sprawdzenie i // przesunięcie }
Przypomnienie ! ! !: Wartością wyrażenia przypisania jest wartość będąca przedmiotem przypisania. Inaczej mówiąc, wyrażenie: • (x = 27) nie tylko wykonuje przypisanie, ale samo jako całość ma wartość 27. Podobnie wyrażenie: • (cel [i] = zrodlo [i] ) ma wartość równą kodowi kopiowanegoznaku.
Wpisywanie łańcuchów do tablic: Kolejny sposób kopiowania znaków: void strcopy (char cel [ ], char zrodlo [ ] ) { int i = 0; while ( cel [i] = zrodlo [i] ) { i++; } } Kompilatory zwykle „podejrzewają” w tym miejscu pomyłkę (znak = omyłkowo zamiast ==) i generują stosowne ostrzeżenie Aby uniknąć ostrzeżeń, lepiej zapisać to tak: while ( (cel [i] = zrodlo [i]) != ‘\0’ ) // lub 0 ale nie ”0”
Przykład: • //……………………………………………………………… • char zrodlo [ ] = { Programowanie komputerów }; • char cel [80]; • strcopy ( cel, zrodlo); • cout << cel; Co byłoby, gdyby została zdefiniowana tablica ? : • char cel [5];
Jeśli tablica cel jest za krótka, to mimo wszystko dalej będzie odbywało się kopiowanie do nieistniejących elementów: cel[5], cel[6], … i tak dalej dopóki łańcuch z tablicy zrodlo nie skończy się. • Mimowolnie będą niszczone komórki pamięci znajdujące się zaraz za naszą tablicą cel. • Aby uniknąć błędów, należy w funkcji umieścić argument określający, ile maksymalnie znaków chcemy przekopiować (np. 5 znaków). • Jeśli łańcuch przeznaczony do kopiowania będzie krótki (np. 3 znaki), to przekopiuje się cały. • Jeśli będzie długi (np. Długi łańcuch”, to przekopiuje się tylko początek “Długi”. • Na końcu musi się oczywiście znaleźć bajt zerowy NULL.
Wniosek: • //……………………………………………………………… • char zrodlo [ ] = { Programowanie komputerów }; • const int MAXLEN = 80; • char cel [MAXLEN]; • strncopy ( cel, zrodlo, MAXLEN-1); • cel[MAXLEN-1] = ‘\0’; • cout << cel; • Używamy stałych !!!! • Przy kopiowaniu należy pamiętać ze użytkownnik może wpowadzić baaaaardzo długie napisy
Przekazywanie łańcucha do funkcji: • Do funkcji wysyłamy adres początku łańcucha, czyli samą jego nazwę bez nawiasów kwadratowych. • Dzięki temu funkcja dowiaduje się, gdzie w pamięci zaczyna się ten łańcuch. • Gdzie on się kończy - funkcja może sprawdzić sama szukając znak NULL.
Tablice wielowymiarowe: Tablice można tworzyć z różnych typów obiektów, w tym również z innych tablic np. : • int X [4] [3]; Tablica X składa się z 4 wierszy i 3 kolumn: • X [0] [0] X [0] [1] X [0] [2] • X [1] [0] X [1] [1] X [1] [2] • X [2] [0] X [2] [1] X [2] [2] • X [3] [0] X [3] [1] X [3] [2]
Tablice wielowymiarowe: • Elementy tablicy umieszcza się pamięci komputera tak, że najszybciejzmienia sięnajbardziej skrajny prawy indeks tablicy. • Zatem poniższa inicjalizacja zbiorcza: • int X [4] [3] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; spowoduje, że elementom tej tablicy zostaną przypisane wartości początkowe: • 1 2 3 • 4 5 6 • 7 8 9 • 10 11 12
Tablice wielowymiarowe: W tablicy int X[4] [3] element X[1] [2] leży w stosunku do początku tablicy o tyle elementów dalej: • (1*3)+ 2 Element X[i] [j] z tablicy o liczbie kolumn 3 leży o • (i*3) + j elementów dalej niż początkowy.
Tablice wielowymiarowe: Wniosek: • do orientacji w tablicy kompilator musi znać liczbę jej kolumn; • natomiast wcale nie musi używać liczby wierszy.
Tablice wielowymiarowe: W jaki sposób przesłać tablicę wielowymiarową do funkcji? Przesyłamy do funkcji tylko adres początku tablicy. Do hipotetycznej funkcji fun przesyłamy tablicę intX[4] [3] w sposób następujący: • fun (X);
Tablice wielowymiarowe: • Jak tablicę odbieramy w funkcji? • Co funkcja musi wiedzieć na temat tablicy? • powinien być znany typ elementów tej tablicy; • aby funkcja mogła łatwo obliczyć sobie, gdzie w pamięci znajduje się określony element, musi znać liczbę kolumn tej tablicy. Deklaracja funkcji fun wygląda tak: • void fun ( int X[ ] [3]);
Tablice wielowymiarowe: • Deklaracja: • void fun2 (int X[4] [3]); • jest również poprawna. • Przez analogię deklaracja funkcji otrzymującej tablicę trójwymiarową ma postać: • void fun3 (int Y[ ] [20] [30]); • a czterowymiarową: • void fun4 ( int Z [ ] [10] [30] [20] );
Sortowanie bąbelkowe 5 5 47 5 5 5 5 5 5 5 17 17 17 47 17 17 17 17 17 17 19 47 5 17 19 19 19 19 19 19 24 19 39 19 47 24 24 24 24 24 47 24 81 39 24 35 35 35 35 35 35 39 24 81 35 47 39 39 39 39 39 81 35 24 39 39 47 43 43 43 43 35 19 35 81 43 43 47 47 47 81 43 43 43 43 52 52 52 52 52 52 52 52 52 52 81 81 81 81 81 etap: 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 indeks elementu