NOVINKA – Víkendový online kurz Software tester, který tě posune dál. Zjisti, jak na to!
NOVINKA - Online rekvalifikační kurz Java programátor. Oblíbená a studenty ověřená rekvalifikace - nyní i online.

Lekce 7 - Pole v jazyce C

V předešlém cvičení, Řešené úlohy k 6. lekci Céčka, jsme si procvičili nabyté zkušenosti z předchozích lekcí.

Dnes si v 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é 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, tento problém nám řeší 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 - Základní konstrukce jazyka C

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

Programovací jazyky se velmi liší v tom, jak s polem pracují. V nižších kompilovaných jazycích, kterým je právě i jazyk C, se musí specifikovat pevná velikost pole ve zdrojovém kódu, která již za běhu nelze měnit. Do pole tedy není možné přidávat další přihrádky a proto musíme myslet na to, aby nám vždy stačilo. Jazyk C nám dále umožňuje vytvořit tzv. dynamicky alokované pole nebo využívat např. spojových seznamů, abychom tento problém obešli. Jedná se však o poměrně pokročilou problematiku, ke které se dostaneme později. Naopak některé interpretované jazyky umožňují nejen deklarovat pole s libovolnou velikostí, ale dokonce tuto velikost na již existujícím poli měnit (např. PHP).

Pro hromadnou manipulaci s prvky pole se používají cykly.

Pole deklarujeme jako běžnou proměnnou, pouze za její název uvedeme hranaté závorky s počtem prvků:

int pole[10];

Slovo pole je samozřejmě název naší proměnné. Nyní máme v proměnné pole pole o velikosti deseti typů int. Jelikož jsme pole teprve založili a operační systém nám pro něj přidělil nějakou paměť, kterou mohla předtím používat jiná aplikace, nemůžeme se spolehnout na to, že jsou v poli samé nuly. Stejně dobře v něm zatím mohou být libovolná náhodná čísla.

K prvkům pole poté přistupujeme opět přes hranatou závorku, pojďme na první index (tedy index 0) uložit číslo 1.

int pole[10];
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:

int pole[10];
int i;
for (i = 0; i < 10; i++)
{
    pole[i] = i + 1;
}

Pozn.: i + 1 do pole ukládáme proto, že i jde od nuly a my chceme do pole uložit čísla od 1.

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

for (i = 0; i < 10; i++)
{
    printf("%d ", pole[i]);
}

Výsledek:

Konzolová aplikace
1 2 3 4 5 6 7 8 9 10

Pole samozřejmě můžeme naplnit ručně a to i bez toho, abychom dosazovali postupně do každého indexu. Použijeme k tomu složených závorek a prvky oddělujeme čárkou:

int cisla[] = {15, 8, 3, 10, 9, 2, 2};

Všimněte si, že nemusíme udávat velikost pole, překladač si ji odvodí z počtu prvků ve výčtu.

Pole často slouží k ukládání mezivýsledků, které se potom dále v programu používají. Když něco potřebujeme 10x, tak to nebudeme 10x počítat, ale spočítáme to jednou a uložíme do pole, odtud poté výsledek jen načteme.

Konstanty

Jelikož musíme uvést velikost pole ve zdrojovém kódu a tuto velikost obvykle používáme na několika místech, hodilo by se ji mít někde uloženou. Není nic horšího, než rozmyslet si, že chceme pole místo 15 prvků velké jen 10 a zapomenout někde v dlouhém kódu nějakou patnáctku přepsat, např. v cyklu, který pole vypisuje. Proto se velikost polí často ukládá do tzv. konstant.

Konstanta je hodnota libovolného datového typu, kterou nelze změnit. Můžeme ji chápat jako proměnnou, ze které lze pouze číst. Využívá se pro případy, kdy chceme definovat v programu nějaké hodnoty, které se sice za jeho běhu nemění, ale my programátoři bychom je občas mohli chtít v kódu změnit v rámci nějaké úpravy. Konstanty definujeme pomocí příkazu #define, jejich názvy je zvykem zapisovat VELKYMI_PISMENY. Hodnotu uvedeme pouze za mezeru nebo tabulátor, není zde rovná se. Definice uvádíme těsně za příkazy #include. Uveďme si kompletní zdrojový kód programu výše tak, aby pro velikost pole využíval konstantu:

#include <stdio.h>
#include <stdlib.h>
#define POCET 10

int main(int argc, char** argv) {
    // Vytvoření pole
    int pole[POCET];

    // Naplnění pole
    int i;
    for (i = 0; i < POCET; i++)
    {
        pole[i] = i + 1;
    }

    // Výpis pole
    for (i = 0; i < POCET; i++)
    {
        printf("%d ", pole[i]);
    }
    return (EXIT_SUCCESS);
}

Pozn.: Příkazy začínající # nejsou příkazy překladače, ale tzv. preprocesoru. To je program, který zpracovává zdrojový kód jako první a vkládá do něj určité úseky kódu, aby to měl překladač následně jednodušší. Preprocesor v našem případě do souboru s programem vloží definice funkcí ze systémových knihoven stdio.h a stdlib.h a dále nahradí každý výskyt POCET za hodnotu 10. Přesněji je konstanta tzv. makrem a preprocesor toho umí ještě mnohem více. Pro naše účely to však nyní opomeneme.

Meze pole

Pozor! Jazyk C žádným způsobem nehlídá, zda se pohybujeme v mezích pole. Je tomu tak kvůli rychlosti. Můžeme tedy např. uložit data na 15. prvek, i když jich má pole pouze 10. Pole je v paměti uloženo jako blok bajtů a céčko počítá podle indexu adresu, na kterou prvek zapíše. Můžeme tedy chybně zapsat na příliš vysoký index a do paměti, která nám nepatří. Tímto způsobem si můžeme nabourat nějaká jiná data v naší aplikaci, která s polem vůbec nesouvisí, ale náhodou byla uložena v paměti za ním. Tyto chyby se obecně velmi těžko hledají a je dobré snažit se jim vyvarovat.

Pole s délkou, kterou určíme až za běhu aplikace

Standard C99 umožňuje deklarovat tzv. VLA (Variable Length Array), pole s délkou, kterou zadáme až za běhu programu. Zafunguje tedy i takovýto kód:

int velikost;
printf("Zadej velikost pole a já ho vytvořím: ");
scanf("%d", &velikost);
int pole[velikost];

Ačkoli je to právě platící standard, je možné, že kód nebude fungovat na všech překladačích. Podobné funkcionality lze docílit také pomocí dynamických polí, se kterými se seznámíme v následující sekci seriálu. Stále platí, že jakmile pole jednou vytvoříme, nemůžeme jeho velikost změnit.

Velikost pole

Pole, které jsme vytvořili, je tzv. staticky alokované. U tohoto typu pole můžeme získat jeho velikost i tímto způsobem:

int pole[10]; // Založíme si pole velikosti 10
printf("Velikost pole je: %d", sizeof(pole)/sizeof(int));

Jednoduše zjistíme kolik bajtů zabírá celé pole a toto číslo vydělíme velikostí datového typu jedné položky. Tím získáme počet položek.

Výsledek:

Konzolová aplikace
Velikost pole je: 10

U dynamických polí a textových řetězců tento způsob použít nelze, spíše si na něj nezvykejte.

Seřazení pole

Velmi často se nám stane, že pole prvků potřebujeme seřadit, např. budeme chtít nalézt nejnižší/nejvyšší plat, počet bodů, náklady, počet kusů a podobně. Ačkoli je tento typ úlohy s oblibou zadáván studentům jako algoritmus k procvičení, standardní knihovna jazyka C poskytuje za tímto účelem funkci qsort(). Její volání je poněkud složitější, takže si ho popíšeme spíše intuitivně. Vůbec nic nám ovšem nebrání, abychom funkci používali již nyní, detailně kód pochopíme až dále v seriálu.

#include <stdio.h>
#include <stdlib.h>

int porovnej(const void * a, const void * b)
{
    return (*(int*)a - *(int*)b);
}

int main(int argc, char** argv)
{
    int cisla[] = {15, 8, 3, 10, 9, 2, 2};

    qsort(cisla, 7, sizeof(int), porovnej);

    int i;
    for (i = 0; i < 7; i++)
    {
        printf("%d ", cisla[i]);
    }
    return (EXIT_SUCCESS);
}

Kromě funkce main() zde máme navíc porovnávací funkci, která definuje jak porovnat 2 prvky v poli. Funkce qsort() totiž vnitřně funguje tak, že porovnává vždy 2 prvky mezi sebou a díky tomu ve finále pole seřadí. Syntaxe hvězdiček si nemusíte všímat, jde o to, že funkce vrací výraz a - b, který je kladný pokud je a > b, nulový pokud a = b a záporný pokud a < b. Podle této hodnoty qsort() poté porovnává. Pokud bychom chtěli pole řadit naopak (sestupně), zadali bychom zde b - a.

Samotné vytvoření pole by mělo být jasné. Při volání funkce qsort() je třeba uvést kromě pole, které má seřadit, také počet jeho prvků, velikost jednoho prvku v bajtech a právě porovnávací funkci. Odteď jsou prvky v poli seřazené a my si je pro kontrolu vypíšeme pomocí cyklu for.

Výsledek:

Konzolová aplikace
2 2 3 8 9 10 15

To by pro dnešek stačilo, můžete si s polem hrát.

V následujícím cvičení, Řešené úlohy k 7. lekci Céčka, si procvičíme nabyté zkušenosti z předchozích lekcí.


 

Předchozí článek
Řešené úlohy k 6. lekci Céčka
Všechny články v sekci
Základní konstrukce jazyka C
Přeskočit článek
(nedoporučujeme)
Řešené úlohy k 7. lekci Céčka
Článek pro vás napsal David Hartinger
Avatar
Uživatelské hodnocení:
63 hlasů
David je zakladatelem ITnetwork a programování se profesionálně věnuje 15 let. Má rád Nirvanu, nemovitosti a svobodu podnikání.
Unicorn university David se informační technologie naučil na Unicorn University - prestižní soukromé vysoké škole IT a ekonomie.
Aktivity