Rozdział 8. Tablice

> Dodaj do ulubionych

Znasz już pojęcie zmiennej i wiesz kiedy używać zmiennych w swoich programach. Nadszedł zatem czas na poznanie jednego z bardziej złożonych typów danych: tablic.

W niektórych programach potrzebnych jest wiele zmiennych tego samego typu, które pełnią podobną rolę. Weźmy na przykład listę użytkowników strony internetowej. Zawiera ona wielką liczbę zmiennych typu string. Albo weźmy dziesięć najlepszych wyników w grze. Są to wartości typu int. Język C++ podobnie jak wszystkie inne języki programowania udostępnia prosty mechanizm grupowania podobnych danych o tym samym typie w wygodnych pakietach. Jak zapewne domyśliłeś się po tytule tego rozdziału, pakiety te nazywają się tablicami.

W rozdziale tym nauczysz się tworzyć dwa rodzaje tablic. Pierwszy typ to tablice, których długość jest znana z góry i ustalona, jak np. lista dziesięciu najlepszych wyników. Natomiast drugi typ to tablice, których długość może się zmieniać, jak np. lista użytkowników strony internetowej.

Jak zapewne się domyślasz, tablice o znanym z góry i stałym rozmiarze są łatwiejsze w użyciu i dlatego zaczniemy od nich.

8.1. Tablice statyczne

We wstępie napisałem, że tablic używa się do przechowywania większych ilości zmiennych tego samego typu. Dlatego też do ich opisu posłużę się przykładem listy najlepszych wyników rewolucyjnej gry komputerowej, którą kiedyś napiszesz.

8.1.1. Przykład użycia tablicy statycznej

Gdybyś chciał wyświetlić listę pięciu najlepszych wyników w grze, w rzeczywistości potrzebowałbyś do tego dwóch list. Jedna z nich służyłaby do przechowywania nazw graczy, a druga — ich osiągnięć. W związku z tym w sumie potrzebowałbyś dziesięciu zmiennych, np.:

string nazwaNajlepszegoGracza1("Nanoc");
string nazwaNajlepszegoGracza2("M@teo21");
string nazwaNajlepszegoGracza3("Albert Einstein");
string nazwaNajlepszegoGracza4("Isaac Newton");
string nazwaNajlepszegoGracza5("Archimedes");

int najlepszyWynik1(118218);
int najlepszyWynik2(100432);
int najlepszyWynik3(87347);
int najlepszyWynik4(64523);
int najlepszyWynik5(31415);

Aby wyświetlić te wszystkie informacje na ekranie, też trzeba się nieźle napracować.

cout << "1) " << nazwaNajlepszegoGracza1 << " " << najlepszyWynik1 << endl;
cout << "2) " << nazwaNajlepszegoGracza2 << " " << najlepszyWynik2 << endl;
cout << "3) " << nazwaNajlepszegoGracza3 << " " << najlepszyWynik3 << endl;
cout << "4) " << nazwaNajlepszegoGracza4 << " " << najlepszyWynik4 << endl;
cout << "5) " << nazwaNajlepszegoGracza5 << " " << najlepszyWynik5 << endl;

Do tego potrzeba bardzo dużo kodu! Wyobraź sobie, co by było, gdybyśmy chcieli wyświetlić wyniki 100 najlepszych graczy. To byłby horror! Trzeba by było zdefiniować 200 zmiennych i napisać 100 prawie identycznych wierszy kodu źródłowego, aby wyświetlić ich zawartość. Tak nie może być! Musimy znaleźć jakiś lepszy sposób.

Ten sposób to użycie tablic. Dzięki tablicy 100 najlepszych wyników i nazwy 100 najlepszych graczy zdefiniujemy za jednym zamachem. Zarezerwujemy w pamięci pojedynczy obszar, który będzie mógł pomieścić 100 liczb typu int i drugi pojedynczy obszar, który będzie mógł pomieścić 100 wartości typu string. Czy to magia?

Zdecydowaliśmy się na utworzenie 100 zmiennych, czyli będziemy potrzebować 100 miejsc w naszej tablicy. Fachowo liczbę miejsc w tablicy nazywa się długością albo po prostu rozmiarem tablicy. Ponieważ długość ta będzie ustawiona na stałe, bez możliwości jej zmiany, utworzona struktura będzie tablicą statyczną. Takiej właśnie potrzebujemy do przechowywania listy 100 najlepszych wyników.

8.1.2. Deklarowanie tablicy statycznej

Deklaracja zmiennej tablicowej, jak wszystkich innych zmiennych w języku C++, składa się z nazwy i typu zmiennej. Ponieważ jednak tablica nie jest typową zmienną, należy do jej deklaracji dodać jeszcze jedną informację: liczbę elementów, jaka będzie w niej przechowywana.

Deklaracja tablicy jest bardzo podobna do deklaracji zwykłej zmiennej:

typ nazwa[długość];

Najpierw określamy typ, następnie podajemy nazwę, a na końcu w nawiasie kwadratowym wpisujemy liczbę elementów. Spójrz na poniższy przykład:

#include <iostream>
using namespace std;

int main()
{
   int najlepszyWynik[5];   // Definicja tablicy pięciu liczb całkowitych

   double katyTrojkata[3];   // Definicja tablicy trzech liczb zmiennoprzecinkowych

   return 0;
}

Poniżej przedstawiam schemat obrazujący nasze dwie tablice w pamięci.

Rysunek 8.1. Schemat przedstawiający tablice w pamięci
Rysunek 8.1. Schemat przedstawiający tablice w pamięci

Na ilustracji widać oba zarezerwowane obszary pamięci wraz z podpisami. Zwróć uwagę, że każdy z nich składa się z kilku elementów — tablica katyTrojkata ma ich trzy, a tablica najlepszyWynik ma ich pięć. Na razie elementy te nie są zainicjowane, a więc ich wartości są nieznane.

Długość tablicy można też określić jako wartość stałej typu int lub unsigned int. W takim przypadku w nawiasie kwadratowym tablicy należy wpisać nazwę wybranej stałej.

int const dlugoscTablicy(20);  // Długość tablicy
double katyDwudziestokata[dlugoscTablicy];

Najlepiej jest długość tablicy zawsze określać przy użyciu stałej, zamiast wpisywać wartość bezpośrednio w nawiasie kwadratowym. To bardzo dobry nawyk.

Skoro zarezerwowaliśmy miejsce w pamięci, trzeba go jakoś użyć.

8.1.3. Pobieranie elementów z tablicy

Elementów tablicy można używać jak zwykłych zmiennych. Jedyna różnica między nimi a zwykłymi zmiennymi dotyczy sposobu dostępu do nich. Aby pobrać z tablicy wartość wybranego elementu, należy podać nazwę tej tablicy i określić numer żądanego elementu. W tablicy najlepszyWynik znajduje się pięć elementów odpowiadających poszczególnym szufladkom pamięci, które widać na schemacie powyżej.

W celu pobrania elementu z tablicy, należy posłużyć się następującą składnią: nazwaTablicy[numerElementu]. Trzeba tylko pamiętać, że numerowanie w tablicach zaczyna się od 0, a więc pierwszy element ma numer 0, drugi — 1 itd. Aby zatem zapisać wartość trzeciego elementu tablicy najlepszyWynik, należy napisać następującą instrukcję:

najlepszyWynik[2] = 5;

Po wykonaniu tej instrukcji przez program, wartość 5 będzie zapisana w elemencie tablicy o numerze 2 (3 – 1 = 2). Aby zatem zapełnić tablicę najlepszyWynik danymi graczy, można napisać następujące instrukcje:

int const liczbaNajlepszychWynikow(5);  // Długość tablicy

int najlepszyWynik[liczbaNajlepszychWynikow];  // Definicja tablicy

najlepszyWynik[0] = 118218; // Zapisanie liczby w pierwszym elemencie
najlepszyWynik[1] = 100432; // Zapisanie liczby w drugim elemencie
najlepszyWynik[2] = 87347; // Zapisanie liczby w trzecim elemencie
najlepszyWynik[3] = 64523; // Zapisanie liczby w czwartym elemencie
najlepszyWynik[4] = 31415; // Zapisanie liczby w piątym elemencie

8.1.4. Przeglądanie zawartości tablicy

Wielką zaletą tablic jest to, że można je przeglądać za pomocą pętli. Dzięki temu możemy na każdym elemencie tablicy po kolei wykonać dowolną operację, np. wyświetlić jego zawartość na ekranie.

Skoro liczbę elementów w tablicy znamy z góry, do jej przeglądania możemy użyć pętli for. Wewnątrz pętli użyjemy zmiennej o nazwie i, za pomocą której w każdej iteracji będziemy uzyskiwać dostęp do i-tego elementu tablicy. Spójrz na poniższy przykładowy kod:

int const liczbaNajlepszychWynikow(5);  // Długość tablicy
int najlepszeWyniki[liczbaNajlepszychWynikow];  // Definicja tablicy

najlepszeWyniki[0] = 118218; // Zapisanie wartości w pierwszym elemencie
najlepszeWyniki[1] = 100432; // Zapisanie wartości w drugim elemencie
najlepszeWyniki[2] = 87347; // Zapisanie wartości w trzecim elemencie
najlepszeWyniki[3] = 64523; // Zapisanie wartości w czwartym elemencie
najlepszeWyniki[4] = 31415; // Zapisanie wartości w piątym elemencie

for(int i(0); i<liczbaNajlepszychWynikow; ++i)
{
    cout << najlepszeWyniki[i] << endl;
}

Zmienna i będzie w poszczególnych cyklach przyjmowała kolejne wartości 0, 1, 2, 3 i 4. Oznacza to, że najpierw do instrukcji cout zostanie wysłany element najlepszeWyniki[0], potem najlepszeWyniki[1] itd.

Z czasem para pętla for i tablica staną się Twoimi najlepszymi przyjaciółmi w programowaniu. Mam przynajmniej taką nadzieję, ponieważ jest to niezwykle przydatny tandem.

8.1.5. Krótki przykład

W tym podrozdziale przedstawię nieco bardziej skomplikowany przykład użycia tablicy. Pokażę Ci program obliczający średnią ocen z całego roku. Wiesz już, jak napisać taki program? Jeśli nie, to czytaj uważnie dalej.

Objaśnię wszystko po kolei. Najpierw musimy utworzyć tablicę na oceny. Ponieważ mogą one zawierać część ułamkową, do ich przechowywania zdefiniujemy tablicę typu double.

int const liczbaOcen(6);
double oceny[liczbaOcen];

Następnie trzeba do tej nowo utworzonej tablicy wstawić oceny. Myślę, że już wiesz, jak to zrobić.

int const liczbaOcen(6);
double oceny[liczbaOcen];

oceny[0] = 4.5;
oceny[1] = 6;  // Brawo!
oceny[2] = 2.;    // Słabo!
oceny[3] = 3;
oceny[4] = 4;
oceny[5] = 5;

Aby obliczyć średnią, należy zsumować wszystkie wartości, a następnie otrzymany wynik podzielić przez liczbę tych wartości. Tę drugą informację już mamy, ponieważ jest ona zapisana w stałej liczbaOcen. Pozostało więc jeszcze tylko utworzyć zmienną do zapisania średniej.

Sumę obliczymy za pomocą pętli for, która przejrzy wszystkie elementy tablicy.

double srednia(0);
for(int i(0); i<liczbaOcen; ++i)
{
   srednia += oceny[i];   // Sumujemy wszystkie oceny
}
// W tym miejscu zmienna srednia zawiera sumę wsyzstkich ocen wynoszącą 79.5
// Pozostaje jeszcze tylko podzielić tę wartość przez liczbę ocen
srednia /= liczbaOcen;

Jeszcze tylko dodać instrukcję wyświetlającą wynik obliczeń na ekranie i program będzie gotowy. Poniżej znajduje się kompletny kod:

#include "stdafx.h";
#include <iostream>
using namespace std;

int main()
{
   int const liczbaOcen(6);
   double oceny[liczbaOcen];

   oceny[0] = 4.5;
   oceny[1] = 6;  // Brawo!
   oceny[2] = 2.;    // Słabo!
   oceny[3] = 3;
   oceny[4] = 4;
   oceny[5] = 5;

   double srednia(0);
   for(int i(0); i<liczbaOcen; ++i)
   {
      srednia += oceny[i];   // Sumujemy wszystkie oceny
   }
   // W tym miejscu zmienna srednia zawiera sumę wsyzstkich ocen wynoszącą 24.5
   // Pozostaje jeszcze tylko podzielić tę wartość przez liczbę ocen
   srednia /= liczbaOcen;

   cout << "Twoja średnia wynosi: " << srednia << endl;
   int y;
   cin >> y;
   return 0;
}

Wynik działania powyższego programu jest następujący:

Twoja średnia wynosi: 4.08333

8.1.6. Tablice statyczne i funkcje

Mam nadzieję, że pamiętasz jeszcze czym są funkcje. Jeśli coś zapomniałeś, to teraz sobie wszystko przypomnisz. Niestety jak się przekonasz funkcje i tablice nie są do siebie nawzajem zbyt przyjaźnie nastawione.

Przede wszystkim nie można napisać funkcji zwracającej tablicę statyczną. Jest to po prostu niemożliwe.

Ponadto tablica statyczna do funkcji jest zawsze przekazywana przez referencję i nie trzeba do tego używać znaku &. Odbywa się to automatycznie. To oznacza, że jeśli przekażemy funkcji tablicę, funkcja będzie ją mogła zmodyfikować.

Poniżej znajduje się przykładowa funkcja przyjmująca tablicę jako argument.

void funkcja(double tablica[])
{
    //...
}

Ale to nie wszystko! Tablice bardzo często przegląda się za pomocą np. pętli for. Brakuje nam ważnej informacji. Domyślasz się jakiej?

Chodzi o długość! W funkcji przedstawionej powyżej nie da się określić długości tablicy bezpośrednio i dlatego trzeba dodać drugi argument, który będzie tę lukę uzupełniał, np.:

void funkcja(double tablica[], int dlugoscTablicy)
{
    //...
}

Tak, wiem że to żmudne, ale to nie moja wina. To nie ja utworzyłem ten język programowania.

W ramach ćwiczenia napisz funkcję o nazwie srednia() obliczającą średnią wartości znajdujących się w tablicy.

Poniżej znajduje się przykładowa wersja takiej funkcji, ale nie zaglądaj do niej dopóki nie napiszesz i nie wypróbujesz własnej.

/*
*  Funkcja obliczająca średnią elementów tablicy
*  - tablica: tablica, dla której ma zostać obliczona średnia
*  - dlugoscTablicy: dlugosc tablicy
*/
double srednia(double tablica[], int dlugoscTablicy)
{
	double srednia(0);
	for(int i(0); i<dlugoscTablicy; ++i)
	{
		srednia += tablica[i];   // Sumowanie wartości wszystkich elementów
	}
	srednia /= dlugoscTablicy;

	return srednia;
}

8.2. Tablice dynamiczne

Na początku napisałem, że w tym rozdziale będzie mowa o dwóch rodzajach tablic. Takich, których rozmiar jest z góry ustalony i niezmienny i takich, których rozmiar może się zmieniać. Pierwszy rodzaj został już omówiony. Są to tzw. tablice statyczne. Natomiast drugi rodzaj tablic, o którym będzie mowa w tej części rozdziału to tablice dynamiczne. Niektóre części opisu tablic statycznych dotyczą także tablic dynamicznych, a więc nie wszystkiego trzeba uczyć się od nowa.

8.2.1. Definiowanie tablicy dynamicznej

Pierwsza różnica między tablicami statycznymi a dynamicznymi jest widoczna już na samym początku programu, ponieważ aby używać tych drugich, trzeba dodać dyrektywę #include <vector>.

Największa różnica między tablicą statyczną a wektorem dotyczy sposobu definiowania. Składnia definicji wektora jest następująca:

vector<TYP> NAZWA(DŁUGOŚĆ)

Aby np. utworzyć wektor zawierający pięć elementów całkowitoliczbowych, należy napisać następujący kod:

#include <iostream>
#include <vector> // Nie zapomnij!
using namespace std;

int main()
{
   vector<int> tablica(5);

   return 0;
}

Należy zapamiętać trzy rzeczy:

  1. W odróżnieniu od definicji innych rodzajów zmiennych, w tym przypadku pierwszym słowem nie jest nazwa typu.
  2. Do definiowania wektorów używa się notacji z użyciem nawiasów ostrych.
  3. Długość wektora określa się w nawiasie okrągłym, a nie kwadratowym.

Wychodzi na to, że definicja wektora w ogóle nie przypomina definicji tablicy statycznej. Ale jak się wkrótce przekonasz, zasady przeglądania obu rodzajów tablic są takie same. Ponadto w przypadku wektorów można stosować pewne ułatwiające pracę sztuczki. Poniżej znajduje się ich opis.

Dodając drugi argument w nawiasie w definicji wektora, można wszystkim jego elementom nadać dowolną wartość domyślną, np.:

vector<int> tablica(5, 3); // Tworzy tablicę 5 elementów, z któych każdy ma wartość 3
vector<string> listaImion(12, "Brak imienia"); // Tworzy tablicę 12 łańcuchów, z których każdy ma wartość "Brak imienia"

Opuszczając nawias można utworzyć tablicę nie zawierającą ani jednego elementu, np.:

vector<double> tablica; // Tworzy tablicę 0 elementów

8.2.2. Uzyskiwanie dostępu do elementów tablicy

Sposób definicji wektora jest całkiem inny niż tablicy statycznej. Na szczęście oba rodzaje tablic przegląda się w identyczny sposób. W tym przypadku również używa się nawiasów kwadratowych, a numerowanie rozpoczyna się od 0.

Możemy zatem przepisać przykład z poprzedniego podrozdziału zastępując w nim tylko definicję tablicy statycznej definicją wektora:

int const liczbaNajlepszychWynikow(5);  // Długość tablicy

vector<int> najlepszeWyniki(liczbaNajlepszychWynikow);  // Definicja tablicy

najlepszeWyniki[0] = 118218; // Zapisanie wartości w pierwszym elemencie tablicy
najlepszeWyniki[1] = 100432; // Zapisanie wartości w drugim elemencie tablicy
najlepszeWyniki[2] = 87347; // Zapisanie wartości w trzecim elemencie tablicy
najlepszeWyniki[3] = 64523; // Zapisanie wartości w czwartym elemencie tablicy
najlepszeWyniki[4] = 31415; // Zapisanie wartości w piątym elemencie tablicy

Prościej już chyba być nie może!

8.2.3. Zmienianie długości tablicy

Skoro mówimy o tablicach dynamicznych, czas w końcu dowiedzieć się, w jaki sposób zmienia się ich rozmiary. Zaczniemy od dodawania elementów na końcu struktury.

Aby dodać element na końcu wektora, należy użyć funkcji push_back(). Najpierw wpisuje się nazwę tablicy, następnie stawia się kropkę, a po kropce wpisuje się funkcję push_back(), w nawiasie której podaje się wartość, która ma zostać dodana na końcu tablicy o podanej wcześniej nazwie:

vector<int> tablica(3,2); // Tablica 3 elemnentów, z których każdy ma wartość 2
tablica.push_back(8);  // Dodajemy czwarty element do tablicy. Będzie on zawierał wartość 8

Spójrz na poniższy rysunek przedstawiający, co podczas wykonywania tej operacji dzieje się w pamięci.

Rysunek 8.2. Schemat ilustrujący dodawanie elementu na końcu wektora
Rysunek 8.2. Schemat ilustrujący dodawanie elementu na końcu wektora

Na końcu tablicy został wstawiony nowy element. Wszystko to odbywa się automatycznie.

Oczywiście nic nie stoi na przeszkodzie, aby dodać do tablicy kilka elementów jeden po drugim.

vector<int> tablica(3,2); // Tablica trzech elementów, z których każdy ma wartość 2
tablica.push_back(8);  // Dodajemy czwarty element o wartości 8
tablica.push_back(7);  // Dodanie piątego elementu o wartości 7
tablica.push_back(14); // Dodanie kolejnego elementu o wartości 14

// Teraz tablica zawiera następujące wartości: 2 2 2 8 7 14

Czy można także usunąć element z wektora? Tak, twórca języka C++ pomyślał także o tym. Do usuwania elementów z wektorów służy funkcja pop_back(), której używa się w taki sam sposób, jak funkcji push_back(), tylko nie wpisuje się niczego w nawiasie.

vector<int> tablica(3,2); // Tablica trzech elementów o wartościach 2
tablica.pop_back(); // Usunięcie jednego elementu
tablica.pop_back(); // Usunięcie drugiego elementu

Pozostała nam jeszcze jedna kwestia do rozwiązania. Ponieważ rozmiar tablicy może się zmieniać, nigdy nie ma pewności, jaki jest jej rozmiar. Na szczęście jest funkcja size() zwracająca liczbę elementów w tablicy:

vector<int> tablica(5,4); // Tablica pięciu elementów o wartościach 4
int const dlugosc(tablica.size()); // Zmienna do zapisania długości tablicy
                                  // Długość tablicy może być różna, ale wartość tej zmiennej będzie stała.
                                  // Dlatego też definiujemy stałą
                                  // Od tego miejsca stała dlugosc będzie miała wartość 5

8.2.4. Powrót do ćwiczenia

Najlepszym sposobem na przyswojenie sobie wszystkich nowych wiadomości będzie powtórzenie ćwiczenia z obliczaniem średniej ocen, tylko z użyciem wektora zamiast tablicy statycznej.

Napisz taki program samodzielnie, a po jego wypróbowaniu porównaj go z moją wersją, która znajduje się poniżej.

#include <iostream>
#include <vector> // Nie zapomnij!
using namespace std;

int main()
{
   vector<double> oceny; // Pusta tablica

   oceny.push_back(4.5);  // Dodajemy elementy z ocenami
   oceny.push_back(6);
   oceny.push_back(2);
   oceny.push_back(3);
   oceny.push_back(4);
   oceny.push_back(5);

   double srednia(0);
   for(int i(0); i<oceny.size(); ++i)  // Granicę dla naszej pętli stanowi wartość notes.size()
   {
      srednia += oceny[i];   // Sumujemy wszystkie oceny
   }

   srednia /= oceny.size();  // Ponownie używamy wywołania notes.size(), aby dowiedzieć się, jaka jest liczba ocen

   cout << "Twoja średnia wynosi: " << srednia << endl;

   return 0;
}

Jak widać, ten program trochę się różni od poprzedniego.

Napisaliśmy dwa programy, które robią dokładnie to samo, ale na różne sposoby. To oznacza, że istnieją różne drogi do tego samego celu i każdy powinien wybrać tę, która mu najbardziej odpowiada.

8.2.5. Wektory i funkcje

Przekazywanie tablic dynamicznych jako argumentów funkcji jest znacznie łatwiejsze niż przekazywanie tablic statycznych. Tak jak w przypadku innych zmiennych, wystarczy tylko podać typ i nazwę argumentu wektorowego. Dzięki dostępności funkcji size() nie trzeba dodawać drugiego argumentu określającego rozmiar tablicy, np.:

void funkcja(vector<int> a)   // Funkcja przyjmująca jako argument tablicę liczb całkowitych
{
    //...
}

Proste, prawda? Ale jak zwykle można coś jeszcze poprawić. W poprzednim rozdziale była mowa o optymalizowaniu wydajności programów poprzez przekazywanie argumentów przez stałą referencję. Technika ta eliminuje konieczność kopiowania przekazywanego argumentu, a jeśli jest nim duża tablica, to pozwala to zaoszczędzić dużo czasu. Oto przykład zastosowania tej techniki dla wektorów:

void funkcja(vector<int> const& a)   // Funkcja przyjmująca jako argument tablicę liczb całkowitych
{
    //...
}

8.3. Tablice wielowymiarowe

Na początku rozdziału napisałem, że można tworzyć tablice różnego rodzaju, liczb całkowitych, łańcuchów itp. Co ciekawe można też tworzyć tablice tablic!

Pomyśl przez chwilę i zastanów się, do czego mogła by się taka wielowymiarowa tablica przydać. Tym razem zrywając z tradycją najpierw pokażę Ci hipotetyczną zawartość pamięci.

Rysunek 8.3. Schemat tablicy wielowymiarowej w pamięci
Rysunek 8.3. Schemat tablicy wielowymiarowej w pamięci

Żółtym kolorem jak zwykle oznaczona jest zmienna. W tym przypadku zmienna ta jest tablicą pięciu elementów oznaczonych grubymi liniami. W każdym z tych elementów znajduje się tablica zawierająca cztery elementy, których wartości nie są znane.

Jeśli uważnie przyjrzysz się powyższej ilustracji, dostrzeżesz że układ tej zmiennej jest regularny i ma postać siatki. Dokonując tego spostrzeżenia na pewno łatwiej Ci będzie to zrozumieć.

8.3.1. Definiowanie tablicy wielowymiarowej

Aby zdefiniować tablicę wielowymiarową, należy dodać tyle nawiasów kwadratowych, ile wymiarów ma mieć tworzona tablica:

typ nazwaTablicy[dlugoscX][dlugoscY]

Aby zatem utworzyć taką tablicę, jak na powyższym rysunku, należy napisać następującą definicję:

int tablica[5][4];

Albo jeszcze lepiej, użyjemy definicji stałych:

int const dlugoscX(5);
int const dlugoscY(4);
int tablica[dlugoscX][dlugoscY];

8.3.2. Uzyskiwanie dostępu do elementów tablic wielowymiarowych

Jestem pewien, że już się domyślasz jak to się robi. Aby uzyskać dostęp do dowolnego elementu tablicy wielowymiarowej, należy podać jego pozycję na osiach X i Y.

Na przykład za pomocą instrukcji tablica[0][0] uzyskamy dostęp do elementu znajdującego się w lewym dolnym rogu tablicy. Instrukcja tablica[0][1] odnosi się do elementu powyżej niego, a tablica[1][0] — do elementu znajdującego się po jego prawej stronie.

A jak uzyskać dostęp do elementu znajdującego się w prawym górnym rogu? Dobre pytanie. Jest to ostatni element po prawej, a więc w pierwszym nawiasie kwadratowym należy wpisać wartość dlugoscX - 1, a w drugim — dlugoscY - 1, czyli należy użyć instrukcji tablica[4][3].

8.3.3. Tablice o większej liczbie wymiarów

Jeśli chcesz, możesz utworzyć tablicę o dowolnej liczbie wymiarów, np. trójwymiarową albo jeszcze inną, np.:

double tablicaWielowymiarowa[5][4][6][2][7];

Niestety nie przedstawię tego na rysunku. W istocie tablice o większej liczbie wymiarów niż dwa spotyka się rzadko. No chyba że masz zamiar pisać niezwykle skomplikowane programy.

8.4. Łańcuchy jako tablice

Na zakończenie rozdziału jak zwykle zachowałem ciekawostkę. Łańcuchy znaków są w rzeczywistości tablicami!

Nie widać tego jednak w definicji zmiennych łańcuchowych, ponieważ jest to ich ukryta właściwość. Zatem łańcuchy mają wiele wspólnego z wektorami.

8.4.1. Uzyskiwanie dostępu do liter

Dzięki temu, że łańcuchy znaków są tablicami liter, możemy uzyskiwać dostęp do wybranych znaków łańcuchów i je modyfikować tak, jakbyśmy działali na tablicach. Zapewne nie zdziwi Cię wiadomość, że do tego celu używa się kwadratowych nawiasów.

#include <iostream>
#include <string>
using namespace std;

int main()
{
   string nazwaUzytkownika("Janek");
   cout << "Nazywasz się " << nazwaUzytkownika << "." <<endl;

   nazwaUzytkownika[0] = 'M';  // Zmieniamy pierwszą literę
   nazwaUzytkownika[1] = 'i';  // Zmieniamy drugą literę
   nazwaUzytkownika[2] = 'r';  // Zmieniamy trzecią literę

   cout << "O nie, ty nazywasz się " << nazwaUzytkownika << "!" << endl;
   int i;
   return 0;
}

Wynik działania tego programu jest następujący:

Nazywasz się Janek. O nie, ty nazywasz się Mirek!

8.4.2. Funkcje i łańcuchy znaków

Za pomocą funkcji size() można sprawdzić długość łańcucha znaków, a przy użyciu funkcji push_back() można dodać na jego końcu dowolną literę. Wszystko robi się tak samo, jak w zwykłym wektorze.

string tekst("Proszę podać whisky tej pięknej blondynce z papierosem.");  // 55 znaków
cout << "To zdanie zawiera "<< tekst.size() << " znaków." << endl;

Jednak w odróżnieniu od tablic, do łańcuchów można dodawać po kilka liter na raz oraz można używać operatora +=.

#include <iostream>
#include <string>
using namespace std;

int main()
{
   string imie("Albert");
   string nazwisko("Einstein");

   string suma;    // Pusty łańcuch
   suma += imie; // Dodajemy imię do pustego łańcucha
   suma += " ";    // Dodajemy spację
   suma += nazwisko;    // Dodajemy nazwisko

   cout << "Nazywasz się " << suma << "." << endl;

   return 0;
}

Wynik działania tego programu jest następujący:

Nazywasz się Albert Einstein.

Na tym zakończymy rozdział o tablicach. Mam nadzieję, że polubiłeś te konstrukcje, ponieważ będą one stanowiły jeden z podstawowych składników budowy Twoich programów.

W następnym rozdziale nauczysz się odczytywać i zapisywać pliki, a później wykonamy pierwszy projekt praktyczny w celu podsumowania wiadomości.

Autor: Mathieu Nebra i Matthieu Schaller

Źródło: http://openclassrooms.com/courses/programmez-avec-le-langage-c/les-tableaux-5

Tłumaczenie: Łukasz Piwko

Treść tej strony jest dostępna na zasadach licencji CC BY-NC-SA 2.0

3 komentarze do “Rozdział 8. Tablice”

  1. Bardzo proszę o pomoc.
    Jestem przy 8.2.5 i gdy rozdzielam funkcje do odzielnych plikow .cpp to pojawia się błąd (robię to dobrze- tak mi się wydaje) w pliku .h

    W pliku .h mam taki nagłówek (czego tutaj brakuje, przecież w tym pliku nie definiujemy żadnych tablic itd.)

    double funkcja1(vector &tablica1);

    Kompilator wskazuje takie błędy:

    D:projekty (nauka programowania)dynamiczneDalejfunkcje.h|4|error: 'vector’ was not declared in this scope|
    D:projekty (nauka programowania)dynamiczneDalejfunkcje.h|4|error: expected primary-expression before 'double’|
    D:projekty (nauka programowania)dynamiczneDalejfunkcja1.cpp|3|error: redefinition of 'double funkcja1’|
    D:projekty (nauka programowania)dynamiczneDalejfunkcje.h|4|error: 'double funkcja1′ previously defined here|
    D:projekty (nauka programowania)dynamiczneDalejfunkcja1.cpp|3|error: 'vector’ was not declared in this scope|
    D:projekty (nauka programowania)dynamiczneDalejfunkcja1.cpp|3|error: expected primary-expression before 'double’|

  2. #include
    #include
    using namespace std;
    int main ()
    {
    int u=0;

    string const haslo=”w”;
    string haslo2;
    cout <>haslo2;
    if (haslo==haslo2)
    {
    int c;
    cout <>c;
    double d[c][/c];
    double w;
    int e;
    for ( e=0;e<c;e++)
    {
    cout <>d[e];
    w+=d[e];

    }
    w/=e;
    cout <<"srednia wynosi "<<w;
    cin.get();
    cin.get();
    return 0;
    }
    else
    cout <<"blad"<<endl;
    cin.get();

    }
    kompiluje mi się normalnie ale wyświetla bzdury prosze o pomoc i w razie potrzeby wyrozumiałość

  3. #include „stdafx.h”;\\Działa, bez tego wpisu!
    #include
    using namespace std;

    int main()
    {
    int const liczbaOcen(6);
    double oceny[liczbaOcen];

    oceny[0] = 4.5;
    oceny[1] = 6; // Brawo!
    oceny[2] = 2.; // Słabo!
    oceny[3] = 3;
    oceny[4] = 4;
    oceny[5] = 5;

    double srednia(0);
    for(int i(0); i<liczbaOcen; ++i)
    {
    srednia += oceny[i]; // Sumujemy wszystkie oceny
    }
    // W tym miejscu zmienna srednia zawiera sumę wsyzstkich ocen wynoszącą 24.5
    // Pozostaje jeszcze tylko podzielić tę wartość przez liczbę ocen
    srednia /= liczbaOcen;

    cout << "Twoja średnia wynosi: " << srednia <> y;
    return 0;
    }

Możliwość komentowania została wyłączona.