IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

7. diel - Polia v jazyku C

V predchádzajúcom cvičení, Riešené úlohy k 6. lekcii Céčka, sme si precvičili získané skúsenosti z predchádzajúcich lekcií.

V minulej lekcii, Riešené úlohy k 6. lekcii Céčka , sme si ukázali cykly. Dnes si v tutoriálu predstavíme dátovú štruktúru poľa a vyskúšame si, čo všetko vie.

Poľa

Predstavte si, že si chcete uložiť nejaké údaje o viac prvkoch. Napr. chcete v pamäti uchovávať 10 čísel, políčka šachovnice alebo mena 50ti užívateľov. Asi vám dôjde, že v programovaní bude nejaká lepšia cesta, než začať búšiť premenné uzivatel1, uzivatel2... až uzivatel50. Nehľadiac na to, že ich môže byť potrebné 1000. A ako by sa v tom potom hľadalo? Brrr, takze nie :)

Ak potrebujeme uchovávať väčšie množstvo premenných rovnakého typu, tento problém nám rieši poľa. Môžeme si ho predstaviť ako rad priehradiek, kde v každej máme uložený jeden prvok. Priehradky sú očíslované tzv. Indexy, prvý má index 0.

štruktúra poľa - Základné konštrukcie jazyka C

(Na obrázku je vidieť pole ôsmich čísiel) Programovacie jazyky sa veľmi líšia v tom, ako s poľom pracujú. V nižších kompilovaných jazykoch, ktorým je práve aj jazyk C, sa musí špecifikovať pevná veľkosť poľa v zdrojovom kóde, ktorá už za behu nemožno meniť. Do poľa teda nie je možné pridávať ďalšie priehradky a preto musíme myslieť na to, aby nám vždy stačilo. Jazyk C nám ďalej umožňuje vytvoriť tzv. Dynamicky alokovanej poľa alebo využívať napr. Spojových zoznamov, aby sme tento problém obišli. Jedná sa však o pomerne pokročilú problematiku, ku ktorej sa dostaneme neskôr. Naopak niektoré interpretované jazyky umožňujú nielen deklarovať pole s ľubovoľnou veľkosťou, ale dokonca túto veľkosť na už existujúcom poli meniť (napr. PHP).

Pre hromadnú manipuláciu s prvkami poľa sa používajú cykly.

Pole deklarujeme ako bežnú premennú, iba za jej názov uvedieme hranaté zátvorky s počtom prvkov:

int pole[10];

Slovo pole je samozrejme názov našej premennej. Teraz máme v premennej pole poľa veľkosti desiatich typov int. Keďže sme polia ešte len založili a operačný systém nám pre neho pridelil nejakú pamäť, ktorú mohla predtým používať iná aplikácia, nemôžeme sa spoľahnúť na to, že sú v poli samé nuly. Rovnako dobre v ňom zatiaľ môžu byť ľubovoľná náhodné čísla.

K prvkom poľa potom pristupujeme opäť cez hranatú zátvorku, poďme na prvý index (teda index 0) uložiť číslo 1.

int pole[10];
pole[0] = 1;

Plniť poľa takto ručne by bolo príliš pracné, použijeme cyklus a naplníme si pole číslami od 1 do 10. K naplneniu použijeme for cyklus:

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

Pozn .: i + 1 do poľa ukladáme preto, že i ide od nuly a my chceme do poľa uložiť čísla od 1.

Aby sme pole vypísali, môžeme za predchádzajúci kód pripísať:

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

výsledok:

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

Pole samozrejme môžeme naplniť ručne a to aj bez toho, aby sme dosadzovali postupne do každého indexu. Použijeme na to zložených zátvoriek a prvky oddeľujeme čiarkou:

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

Všimnite si, že nemusíme udávať veľkosť poľa, prekladač si ju odvodí z počtu prvkov vo výpočte.

Pole často slúži na ukladanie medzivýsledkov, ktoré sa potom ďalej v programe používajú. Keď niečo potrebujeme 10x, tak to nebudeme 10x počítať, ale spočítame to raz a uložíme do poľa, odtiaľ potom výsledok len načítame.

Konštanty

Keďže musíme uviesť veľkosť poľa v zdrojovom kóde a túto veľkosť zvyčajne používame na niekoľkých miestach, hodilo by sa ju mať niekde uloženú. Nie je nič horšie, než rozmyslieť si, že chceme pole miesto 15 prvkov veľké len 10 a zabudnúť niekde v dlhom kóde nejakú pätnástku prepísať, napr. V cykle, ktorý polia vypisuje. Preto sa veľkosť polí často ukladá do tzv. Konštánt.

Konštanta je hodnota ľubovoľného dátového typu, ktorý sa nemôže zmeniť. Môžeme ju chápať ako premennú, z ktorej možno iba čítať. Využíva sa pre prípady, keď chceme definovať v programe nejaké hodnoty, ktoré sa síce za jeho behu nemení, ale my programátori by sme ich občas mohli chcieť v kóde zmeniť v rámci nejaké úpravy. Konstanty definujeme pomocou príkazu #define, ich názvy je zvykom zapisovať VELKYMI_PISMENY. Hodnotu uvedieme iba za medzeru alebo tabulátor, nie je tu rovná sa. Definícia uvádzame tesne za objednávok #include. Uveďme si kompletný zdrojový kód programu vyššie tak, aby pre veľkosť poľa využíval konštantu:

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

int main(int argc, char** argv) {
    // Vytvorenie poľa
    int pole[POCET];

    // Naplnenie poľa
    int i;
    for (i = 0; i < POCET; i++)
    {
        pole[i] = i + 1;
    }

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

Pozn .: Príkazy začínajúcich # nie sú príkazy prekladača, ale tzv. Preprocesoru. To je program, ktorý spracováva zdrojový kód ako prvý a vkladá do neho určitej úseky kódu, aby to mal prekladač následne jednoduchšie. Preprocesor v našom prípade do súboru s programom vloží definície funkcií zo systémových knižníc stdio.h a stdlib.h a ďalej nahradí každý výskyt POCET za hodnotu 10. Presnejšie je konštanta tzv. Makrom a preprocesor toho vie ešte oveľa viac. Pre naše účely to však teraz zabudneme.

Medze pole

Pozor! Jazyk C žiadnym spôsobom nestráži, či sa pohybujeme v medziach poľa. Je tomu tak kvôli rýchlosti. Môžeme teda napr. Uložiť dáta na 15. prvok, aj keď ich má pole iba 10. Pole je v pamäti uložené ako blok bajtov a céčko počíta podľa indexu adresu, na ktorú prvok zapíše. Môžeme teda chybne zapísať na príliš vysoký index a do pamäti, ktorá nám nepatrí. Týmto spôsobom si môžeme nabúrať nejaká iná dáta v našej aplikácii, ktorá s poľom vôbec nesúvisí, ale náhodou bola uložená v pamäti za ním. Tieto chyby sa všeobecne veľmi ťažko hľadajú a je dobré snažiť sa im vyvarovať.

Polia s dĺžkou, ktorú určíme až za behu aplikácie

Standard C99 umožňuje deklarovať tzv. VLA (Variable Length Array), polia s dĺžkou, ktorú zadáme až za behu programu. Zafunguje teda aj takýto kód:

int velikost;
printf("Zadaj veľkosť poľa a ja ho vytvorím: ");
scanf("%d", &velikost);
int pole[velikost];

Hoci je to práve platiaci štandard, je možné, že kód nebude fungovať na všetkých prekladačom. Podobné funkcionality možno docieliť aj pomocou dynamických polí, s ktorými sa zoznámime v nasledujúcej sekcii seriálu. Stále platí, že akonáhle polia raz vytvoríme, nemôžeme jeho veľkosť zmeniť.

Veľkosť poľa

Pole, ktoré sme vytvorili, je tzv. Staticky alokované. U tohto typu poľa môžeme získať jeho veľkosť aj týmto spôsobom:

int pole[10]; // Založíme si pole veľkosti 10
printf("Veľkosť poľa je: %d", sizeof(pole)/sizeof(int));

Jednoducho zistíme koľko bajtov zaberá celé pole a toto číslo vydelíme veľkosťou dátového typu jednej položky. Tým získame počet položiek.

výsledok:

Konzolová aplikácia
Veľkosť poľa je: 10

U dynamických polí a textových reťazcov tento spôsob použiť nedá, skôr si na neho nezvykajte.

Zoradenie polia

Veľmi často sa nám stane, že pole prvkov potrebujeme zoradiť, napr. Budeme chcieť nájsť najnižšie / najvyšší plat, počet bodov, náklady, počet kusov a podobne. Hoci je tento typ úlohy s obľubou zadávaný študentom ako algoritmus na precvičenie, štandardná knižnica jazyka C poskytuje na tento účel funkciu qsort(). Jej volanie je trochu zložitejšie, takže si ho popíšeme skôr intuitívne. Vôbec nič nám však nebráni, aby sme funkciu používali už teraz, detailne kód pochopíme až ďalej v seriáli.

#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);
}

Okrem funkcie main() tu máme navyše porovnávaciu funkciu, ktorá definuje ako porovnať 2 prvky v poli. Funkcia qsort() totiž vnútorne funguje tak, že porovnáva vždy 2 prvky medzi sebou a vďaka tomu vo finále poľa zoradí. Syntax hviezdičiek si nemusíte všímať, ide o to, že funkcia vracia výraz a - b, ktorý je kladný ak je a > b, nulový ak a = b a záporný ak a < b. Podľa tejto hodnoty qsort() potom porovnáva. Ak by sme chceli pole radiť naopak (zostupne), zadali by sme tu b - a.

Samotné vytvorenie poľa by malo byť jasné. Pri volaní funkcie qsort() je potrebné uviesť okrem pole, ktoré má zoradiť, tiež počet jeho prvkov, veľkosť jedného prvku v bajtoch a práve porovnávaciu funkciu. Odteraz sú prvky v poli zoradené a my si ich pre kontrolu vypíšeme pomocou cyklu for.

výsledok:

Konzolová aplikácia
2 2 3 8 9 10 15

To by pre dnešok stačilo, môžete si s poľom hrať. V budúcej lekcii, Riešené úlohy k 7. lekcii Céčka , si konečne uvedieme ako pracovať s textovými reťazcami a naučíme sa ukladať text do premenných :-)

V nasledujúcom cvičení, Riešené úlohy k 7. lekcii Céčka, si precvičíme nadobudnuté skúsenosti z predchádzajúcich lekcií.


 

Predchádzajúci článok
Riešené úlohy k 6. lekcii Céčka
Všetky články v sekcii
Základné konštrukcie jazyka C
Preskočiť článok
(neodporúčame)
Riešené úlohy k 7. lekcii Céčka
Článok pre vás napísal David Hartinger
Avatar
Užívateľské hodnotenie:
2 hlasov
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 sa informačné technológie naučil na Unicorn University - prestížnej súkromnej vysokej škole IT a ekonómie.
Aktivity