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.