9. díl - Pole v C++

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

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

Minule jsme si v našem C++ seriálu ukázali ošetření uživatelských vstupů. Dnes si 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, nyní nám bude muset stačit, že se k jejich zápisu používá hvězdička a že když poté odečteme tento ukazatel od pole, získáme skutečnou pozici hodnoty v poli. Pokud funkce prvek nenalezne, vrátí délku pole. 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. Příště 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?
4 hlasů
Autor se věnuje spoustě zajímavých věcí :)
Miniatura
Všechny články v sekci
Základní konstrukce jazyka C++
Miniatura
Následující článek
Cvičení k 8.-9. lekci C++
Aktivity (11)

 

 

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

Avatar
Michal Rivola:19.12.2016 20:50

Ahoj :D mohl bych se zeptat kde sem udělal chybu :D

 
Odpovědět 19.12.2016 20:50
Avatar
Odpovídá na Michal Rivola
Petr Štechmüller:19.12.2016 20:53

Ahoj, nemažeš pole intů
Takto ho definuješ:

int pole[10];
// teď by jsi ho měl vyčistit (vynulovat) pomocí memset

V C nemáš předem jisté, že paměť o kterou jsi si řekl bude prázdná

Odpovědět 19.12.2016 20:53
Pokud spolu kód a komentář nekorespondují, budou patrně oba chybné
Avatar
Odpovídá na Petr Štechmüller
Michal Rivola:19.12.2016 20:55

jo díky :D já sem zatím hodně začátečník takže si budu muset ještě najít jak se dělá s memset :D

 
Odpovědět 19.12.2016 20:55
Avatar
Cement
Člen
Avatar
Cement:9. února 16:37

Jak velké pole můžu nadefinovat? Zkoušel jsem pole o velikosti 20 milionů. Zkompilovalo se to v pořádku. Po spuštěni exe souboru mi program spadl a Win10 hledalo řešení problému.
Pod Ubuntu je to podobné. Jen tam program bere pole int o max velikosti 2 miliony. Ve Win10 max 500 tisíc. Když vytvořím pole bool, tak velikost je asi 4x větší.
V Javě dokáže vytvořit pole velké přes 200 milionů.
Víte někdo, kde je zakopaný pes v C++?
Děkuji za odpovědi.

 
Odpovědět 9. února 16:37
Avatar
Odpovídá na Cement
Lukáš Hruda (Luckin):9. února 17:13

Předpokládám, že to pole vytváříš staticky:

int pole[500000];

V takovém případě je velikost pole omezena velikostí zásobníku, která ve Windows je tuším 1MB.
Pokud pole vytvoříš dynamicky:

int* pole = new int[1000000000];

Takto můžeš teoreticky vytvořit pole skoro tak velké, jako je celý tvůj adresní prostor.
Tedy, ne tak docela, protože ti to může zhatit fragmentace paměti, ale každopádně takto můžeš vytvořit mnohem větší pole, klidně i větší než 200 milionů prvků.

 
Odpovědět  +1 9. února 17:13
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Petr Štechmüller
DarkCoder:9. února 21:46

V C nemáš předem jisté, že paměť o kterou jsi si řekl bude prázdná

Nemáš tak úplně pravdu. Toto platí o lokálních proměnných. Lokální proměnné, které nejsou inicializovány mohou totiž obsahovat libovolnou neznámou hodnotu. I když některé překladače jazyka C nastavují neinicializované proměnné na nulu, určitě bych se na to nespoléhal. Kdežto globální proměnné, které nejsou explicitně inicializovány, jsou automaticky nastaveny na nulu. Stejně tak vytvářím-li proměnnou dynamicky pomocí funkce calloc(), je po úspěšné alokaci inicializován každý prvek na nulu.

 
Odpovědět 9. února 21:46
Avatar
Zbyňa
Člen
Avatar
Zbyňa:9. dubna 2:05

vůbec tomuhle nerozumím ... popište mi tedy rozdíl mezi lokální a globální proměnnou základního datového typu v c a c++ tak berme v úvahu tenhle kód

int main()
{
  int local;
  cin >> local;
  int* policko = new int[local];
  // your code
 delete [] policko;
}

/////////////////// dalsi kod k porovnani

int *p;
int i;
int main()
{
  cin >> i;
  *p = new int[i];
// tvuj kod
delete [] p;
 return 0;
}
Odpovědět 9. dubna 2:05
čistý kód
Avatar
Dan Balarin
Člen
Avatar
Odpovídá na Zbyňa
Dan Balarin:9. dubna 3:04

K prvnímu kódu:
Nemáš tam globální proměnnou, ta se deklaruje mimo funkci. To co ty tam máš jsou dvě proměnné, první uložena v zásobníku(stack) a druhá na haldě(heap).
K druhému kódu:
Obě proměnné jsou globální, první uložená na haldě a druhá v zásobníku. Před zavoláním funkce main obě mají definovanou hodnotu, první obsahuje nullptr(null pointer) a druhá 0.

Rozdíl:

int main()
{
  int local;
  cout << local << endl; // Nedefinované chování
}

vs.

int local;
int main()
{
  cout << local << endl; // 0
}
Odpovědět 9. dubna 3:04
"The number one benefit of information technology is that it empowers people to do what they want to do. It lets...
Avatar
Zbyňa
Člen
Avatar
Odpovídá na Dan Balarin
Zbyňa:9. dubna 21:04

1 kod mimo tělo funkce main mám tam deklarovanou proměnnou i a ukazatel pouze ukazatel pls neslovíčkař já vím já vím účel toho bylo alokovat dynamické pole mimo funkci main pomocí operátoru new[] a pak to same smazat pomoci operatoru delete[] pole; blbec jsem tam mohl dat i verzi s malloc ale jsem linej jak ves XD muzes me za to lincovat nebo machat ve Vltave XD takze este raz bez kodu chtel jsem mit globalni alokaci dynamickeho pole napriklad int a v druhe verzi lokalniho. Pozor tady sranda nekonci to same s verzi malloc a calloc a free jestli mi rozumis dekuji za dalsi odpoved

Odpovědět 9. dubna 21:04
čistý kód
Avatar
Zbyňa
Člen
Avatar
Zbyňa:9. dubna 21:08

ah krom toho to byl ucel mit dve verze programu s ruznymi stytly alokace pameti a tak jsi mi odpovedel takze vlastne dekuji predchozi prispevek ber prosim jako novou otazku dekuji mockrat za trpelivost a ochotu

Editováno 9. dubna 21:09
Odpovědět 9. dubna 21:08
čistý kód
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 15. Zobrazit vše