Přidej si svou IT školu do profilu a najdi spolužáky zde na síti :)

8. díl - Pole v C++

C a C++ C++ Základní konstrukce C++ Pole v C++

Unicorn College ONEbit hosting Tento obsah je dostupný zdarma v rámci projektu IT lidem. Vydávání, hosting a aktualizace umožňují jeho sponzoři.

V minulé lekci, Cykly v C++ (while, do while), jsme si ukázali další typy cyklů a naučili se používat příkazy break a continue. Dnes si v C++ tutoriálu představíme datovou strukturu pole a vyzkoušíme si, co všechno umí.

Pole

Představte si, že si chcete uložit nějaké údaje o více prvcích. Např. chcete v paměti uchovávat 10 čísel, políčka šachovnice nebo jména 50ti uživatelů. Asi vám dojde, že v programování bude nějaká lepší cesta, než začít "bušit" proměnné s názvy uzivatel1, uzivatel2... až uzivatel50. Nehledě na to, že jich může být třeba 1000. A jak by se v tom potom hledalo? Brrr, takle ne :)

Pokud potřebujeme uchovávat větší množství proměnných stejného typu, můžeme použít pole. Můžeme si ho představit jako řadu přihrádek, kde v každé máme uložený jeden prvek. Přihrádky jsou očíslované tzv. indexy, první má index 0.

Struktura pole

(Na obrázku je vidět pole osmi čísel)

Programovací jazyky se velmi liší v tom, jak s polem pracují. V některých jazycích (zejména kompilovaných) není možné za běhu programu vytvořit pole s dynamickou velikostí (např. mu dát velikost dle nějaké proměnné). Pole se zde musí deklarovat s konstantní velikostí přímo ve zdrojovém kódu. Lze to obejít pointery a vlastními datovými strukturami. Naopak některé modernější interpretované jazyky nebo jazyky s virtuálním strojem umožňují nejen deklarovat pole s libovolnou velikostí, ale dokonce tuto velikost na již existujícím poli měnit (např. PHP). C++ je jazyk čistě kompilovaný, u pole tedy budeme vždy velikostně omezeni.

Možná vás napadá, proč se tu zabýváme s polem, když má evidentně mnoho omezení a existují lepší datové struktury. Odpověď je prostá: pole je totiž jednoduché. Nemyslím pro nás na pochopení (to také), ale zejména pro počítač. Rychle se s ním pracuje, protože prvky jsou v paměti jednoduše uloženy za sebou, zabírají všechny stejně místa a rychle se k nim přistupuje. Je to klíčová struktura. Pro hromadnou manipulaci s prvky pole se poté používají cykly.

Pole deklarujeme pomocí hranatých závorek, ve kterých uvedeme jeho velikost. Před název proměnné nezapomene uvést datový typ jeho prvků, zde celá čísla:

int pole[16];

pole je samozřejmě název naší proměnné.

Velikost pole je pevně zadaná číslem ve zdrojovém kódu. Schválně si zkuste místo čísla použít proměnnou. Visual Studio to označí za chybu.

Pokud nám pevná velikost pole stačí, ale chceme jeho velikost zadat proměnnou (např. abychom ji pak použili v podmínce v cyklu), musíme použít tzv. konstantu. To je proměnná, jejíž hodnotu nelze měnit. Vytváří se téměř stejně jako běžné proměnné, jen před datový typ napíšeme klíčové slovo const:

const int delkaPole = 24;
int pole[delkaPole];

Ohledně změny délky jsme si sice za běhu programu nepomohli, nicméně, když si jako programátoři rozmyslíme, že chceme zadat napevno jiný počet prvků, změní se konstanta na všech místech v programu. Bez konstant, kdybychom pole poté vypisovali cykly, mohli bychom např. zapomenou změnit počet prvků i tam a z pole vyjet nebo jej nevypsat celé.

Často potřebujeme délku pole zadat pomocí obyčejné proměnné. Takové pole musíme založit tzv. dynamicky, což se naučíme v navazujícím C++ kurzu. Probírat tuto problematiku hned v úvodním kurzu by určitě nebyl dobrý nápad.

K prvkům pole přistupujeme přes hranatou závorku. Pojďme na první index (tedy index 0) uložit číslo 1.

const int delkaPole = 24;
int pole[delkaPole];
pole[0] = 1;

Plnit pole takto ručně by bylo příliš pracné, použijeme cyklus a naplníme si pole čísly od 1 do 10. K naplnění použijeme for cyklus:

const int delkaPole = 10;
int pole[delkaPole];
for (int i = 0; i < delkaPole; i++)
        pole[i] = i + 1;

Všimněte si, že délku pole máme opět uloženou v konstantě.

Pozn.: Nikdy se nesnažte přistupovat k prvkům za koncem pole (např k 20. prvku v poli o 10 prvcích). V lepším případě dostanete nedefinovaný výsledek (hodnoty, co byly zrovna náhodou uloženy v paměti), v tom horším případě program spadne.

Abychom pole vypsali, můžeme za předchozí kód připsat:

for (int i = 0; i < delkaPole; i++)
        cout << pole[i] << ' ';

Naplnit pole hodnotami můžeme ještě jedním způsobem. Pokud známe jeho prvky předem, můžeme využít následující syntaxi:

int pole[5] = {1, 2, 3, 4, 5};

Číslo v hranatých závorkách lze vynechat, délka pole se poté určí podle počtu prvků ve složených závorkách. Na druhou stranu můžeme v hranatých závorkách uvést hodnotu větší než počet prvků ve složených závorkách. Pole poté nebude mít všechny prvky nastavené.

Funkce pro práci s poli

Mnoho užitečných funkcí pro práci s poli můžeme najít v souboru "algorithm". Některé z nich si tu ukážeme (nesmíme zapomenout umístit #include <algorithm> na začátek souboru).

find()

Funkce find() nám vrací tzv. ukazatel na první výskyt hodnoty, kterou jsme jí předali. S ukazateli se teprve setkáme, ale abychom mohli hledání v poli používat již nyní, naučíme se funkci volat již teď. Pole je vnitřně ukazatel, který ukazuje na určité místo v paměti, kde pole začíná (např. může ukazovat na adresu 100). Funkce find() nám vrátí ukazatel na místo v paměti, kde je prvek, který hledáme (např. na adresu 120). Ukazatel deklarujeme pomocí hvězdičky. Pokud od této adresy odečteme adresu začátku pole, získáme s těmito čísly hodnotu 20, což je pozice prvku (prvek byl 20 pozic od začátku pole). Pokud vás to zmátlo, používejte funkci zatím pouze intuitivně, s ukazateli se ještě bohatě setkáme dále v kurzu a vše si velmi podrobně vysvětlíme. Byla by ovšem škoda psát si hledací funkci pokaždé znovu, když ji můžeme použít již nyní.

Pojďme si vše ukázat na příkladu a nechme si najít pozici hodnoty 7 v poli několika čísel:

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

int main(void)
{
        const int delkaPole = 5;
        int pole[delkaPole] = { 1, 7, 3, 4, 10 };
        int hledanyPrvek = 7;
        int *i = find(pole, pole + delkaPole, hledanyPrvek);
        int pozice = i - pole;
        if (pozice < delkaPole)
                cout << "Prvek " << hledanyPrvek << " nalezen na pozici: " << pozice << "." << endl;
        else
                cout << "Prvek nenalezen." << endl;
        cin.get();
        return 0;
}

count()

const int delkaPole = 6;
int pole[delkaPole] = { 1, 6, 9, 2, 6, 3 };
int c = count(pole, pole + delkaPole, 6);
cout << c; // c = 2
cin.get();

Funkce count() nám spočítá, kolikrát se v poli vyskytuje předaná hodnota - v ukázce 6.

copy()

const int delkaPole = 5;
int pole[delkaPole] = { 1, 2, 3, 4, 5 };
int pole2[delkaPole] = { 0, 0, 0, 0, 0 };
copy(pole, pole + delkaPole, pole2);
// pole = { 1, 2, 3, 4, 5 }, pole2 = { 1, 2, 3, 4, 5 }
for (int i = 0; i < delkaPole; i++)
{
        cout << pole[i] << "->" << pole2[i] << " ";
}
cin.get();

Funkce copy() zkopíruje obsah jednoho pole do druhého.

Pozn.: druhé pole musí být stejně dlouhé jako první pole, nebo delší.

max_element()

const int delkaPole = 5;
int pole[delkaPole] = { 2, 1, 4, 5, 3 };
int *i = max_element(pole, pole + delkaPole);
cout << *i; // i = pole + 3, *i = 5
cin.get();

Funkce max_element() vrací ukazatel na největší hodnotu v poli. Hodnoty porovnává pomocí operátoru <. Pokud se nejvyšší hodnota vyskytuje v poli několikrát, vrácený ukazatel bude ukazovat na její první výskyt. Pozici získáme stejně jako u příkladu s find().

min_element()

const int delkaPole = 5;
int pole[delkaPole] = { 2, 1, 4, 5, 3 };
int *i = min_element(pole, pole + delkaPole);
cout << *i; // i = pole + 1, *i = 1
cin.get();

Funkce min_element() je obdoba předchozí funkce s jediným rozdílem - vrací ukazatel na nejnižší hodnotu v poli. Pozici získáme stejně jako u příkladu s find().

sort()

const int delkaPole = 5;
int pole[delkaPole] = { 2, 1, 4, 5, 3 };
sort(pole, pole + delkaPole);
// pole = { 1, 2, 3, 4, 5 }
for (int i = 0; i < delkaPole; i++)
{
        cout << pole[i] << " ";
}
cin.get();

Sort() vzestupně seřadí prvky v poli (pomocí operátoru < ).

fill()

const int delkaPole = 5;
int pole[delkaPole] = { 1, 2, 3, 4, 5 };
fill(pole, pole + delkaPole, 5);
// pole = { 5, 5, 5, 5, 5 }
for (int i = 0; i < delkaPole; i++)
{
        cout << pole[i] << " ";
}
cin.get();

Funkce fill() naplní pole zadanou hodnotou.

reverse()

const int delkaPole = 5;
int pole[delkaPole] = { 1, 2, 3, 4, 5 };
reverse(pole, pole + delkaPole);
// pole = { 5, 4, 3, 2, 1 }
for (int i = 0; i < delkaPole; i++)
{
        cout << pole[i] << " ";
}
cin.get();

Funkce reverse() "převrátí" obsah pole. To znamená, že jeho hodnoty budou pozpátku.

random_shuffle()

#include <iostream>
#include <algorithm>
#include <ctime>
using namespace std;

int main(void)
{
        srand(unsigned(time(0))); // inicializuje generátor náhodných čísel
        const int delkaPole = 5;
        int pole[delkaPole] = { 1, 2, 3, 4, 5 };
        random_shuffle(pole, pole + delkaPole);
        // např. pole = { 3, 1, 5, 2, 4 }
        for (int i = 0; i < delkaPole; i++)
        {
                cout << pole[i] << " ";
        }
        cin.get();
        // Prosím, pamatujte, že pokud používáte náš online kompiler, výsledky jsou
        // cachované a zůstávají tedy stejné po několik dní. Pro vynucení nové kompilace
        // a tedy vygenerování nových náhodných čísel je třeba kód upravit, stačí upravit
        // i jen komentář.
        return 0;
}

Funkce random_shuffle() náhodně přehází prvky v poli.

To by pro dnešek stačilo, můžete si s polem hrát. V příští lekci, Řetězce v jazyce C++, se začneme věnovat práci s textem v C++.


 

 

Článek pro vás napsal Zdeněk Pavlátka
Avatar
Jak se ti líbí článek?
5 hlasů
Autor se věnuje spoustě zajímavých věcí :)
Miniatura
Předchozí článek
Cykly v C++ (while, do while)
Miniatura
Všechny články v sekci
Základní konstrukce jazyka C++
Miniatura
Následující článek
Cvičení k 8. lekci C++
Aktivity (15)

 

 

Komentáře
Zobrazit starší komentáře (27)

Avatar
Lukáš Hruda (Luckin):19. září 0:20

Netvrdil jsem, ze je to napsane spatne. Priznam se, ze jsem ani necetl bezprostredne predchozi ani nasledujici clanek, narazil jsem na tento, protoze se mi objevil na hlavni strane a byl jsem zvedavy. Tohle je jen vec, ktera me zaujala a ktera by treba mne osobne, pokud bych C++ neumel a pouzival clanek k uceni, trochu vadila.
Tvrdit, ze je to nejlepsi mozne je velmi odvazne, protoze objektivne nic jako nejlepsi mozne neexistuje, kazdemu vyhovuje neco jineho a kazdemu i vadi neco jineho. Stejne jako tvrdit ze zacit pointery je nudne a slozite, opet je to subjektivni, kazdemu prijde nudne a slozite neco jineho. Nekomu jinemu to takto vadit nemusi a muzou mu zase vadit jine veci. Napsal jsem jen svuj subjektivni nazor.
O tom, ze k poli nejsou potreba ukazatele, by se teoreticky dalo polemizovat, jelikoz v C/C++ i staticke pole je v podstate jen ukazatel na blok pameti na zasobniku, ale to je jen formalni zalezitost. Prakticky mas pravdu, ze k zakladni praci s poli to neni potreba znat.

 
Odpovědět 19. září 0:20
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Lukáš Hruda (Luckin)
David Čápka:19. září 10:17

Tvrdit, ze je to nejlepsi mozne je velmi odvazne, protoze objektivne nic jako nejlepsi mozne neexistuje, kazdemu vyhovuje neco jineho a kazdemu i vadi neco jineho.

Hovořil jsem o nejlepším řešení pro ITnetwork. Každému sice vyhovuje něco jiného, ale populární kurzy by měly být napsané tak, jak to vyhovuje většině, a to pointery prostě nejsou, už jen vzhledem k tomu, jak se jich zbavily ostatní jazyky.

Odpovědět 19. září 10:17
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
Jiří Gracík
Redaktor
Avatar
Odpovídá na David Čápka
Jiří Gracík:19. září 11:45

Mají ty kurzy nějaký feedback? A je někde případně zveřejněný?

Odpovědět 19. září 11:45
Creating websites is awesome till you see the result in another browser ...
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Jiří Gracík
David Čápka:19. září 18:39

Ano, mám odučeno stovky lidí osobně a z 99% mám pozitivní feedback, ale to snad víš, že jsem dřív učil, než jsem začal psát články na fulltime.

Odpovědět 19. září 18:39
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
Jiří Gracík
Redaktor
Avatar
Odpovídá na David Čápka
Jiří Gracík:20. září 1:54

Vím že jsi učil, ale ptal jsem se na ten feedback. Bylo by zajímavý si to pročíst a třeba by to zajímalo mimojiné i nějaký budoucí zájemce o kurzy.

Odpovědět  +1 20. září 1:54
Creating websites is awesome till you see the result in another browser ...
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Jiří Gracík
David Čápka:20. září 10:05

Na takový feedback se nepodíváš, protože jde o reakce stovek lidí na určité postupy ve výuce :) Osobní reakce, doplním, aby nedošlo opět k omylu.

Editováno 20. září 10:06
Odpovědět 20. září 10:05
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
Filistin
Člen
Avatar
Filistin:23. září 7:17

Asi se to dozvím až přejdu k ukazatelům ... ake zkusim se zeptat.

U tý funkce find() ...
Pozici získám tak, že odečtu i a pole.
Když si do konzole oba objekty vypíšu, dostanu něco takovýho:
0x28fe84 a* 0x28fe80*

To jsou adresy v paměti, pod kterými pak najdu dané hodnoty?

Tak jsem to pochopil, protože když pak vypíšu ** i** a ** *pole**, dostanu hodnoty z mýho pole:
*4(hodnota, co hledám) a 21(první prvek v poli)

Co mi nedává smysl je, jaktože když odečtu dvě adresy (pokud jde tedy o adresy a taky pokud jde vůbec o odečítaní), dostanu pozici to mýho hledanýho prvku.

 
Odpovědět 23. září 7:17
Avatar
patrik.valkovic
Šéfredaktor
Avatar
Odpovídá na Filistin
patrik.valkovic:23. září 8:30

Je na celý jeden díl v seriálu (konkrétně se jedná o aritmetiku ukazatelů).

Odpovědět 23. září 8:30
Nikdy neumíme dost na to, abychom se nemohli něco nového naučit.
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Filistin
David Čápka:23. září 15:55

Upravil jsem popisek u funkce, snad to bude teď jasnější.

Odpovědět 23. září 15:55
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
Filistin
Člen
Avatar
Filistin:23. září 17:22

Díky za odpovědi.
Projdu si pak ty ukazatele dopodrobna a vrátim se sem a snad tomu porozumim maximalně už pak.
Jinak ... parádní tutoriály ... ze školy mě c++ hrozně děsilo ,ale zatim to neni tak strašný - uvidim samozřejmě ale až se dostanku k objektovýmu programování. :D

 
Odpovědět 23. září 17:22
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 37. Zobrazit vše