Toggle menu
Toggle preferences menu
Toggle personal menu
Neprihlásený/á
Your IP address will be publicly visible if you make any edits.
Bez shrnutí editace
Bez shrnutí editace
Značky: manuálne vrátenie vizuálny editor
 
(4 medziľahlé úpravy od rovnakého používateľa nie sú zobrazené.)
Riadok 70: Riadok 70:
Každý skompilovateľný C súbor musí obsahovať funkciu <code>main</code>. Je to funkcia, ktorá sa pri spustení programu v terminály zavolá vždy ako prvá - je to hlavný vstupný bod do programu. Táto funkcia môže potom volať ďalšie funkcie ktoré boli predtým definované, čo nám umožňuje logiku rozkúskovať na viaceré časti, pre lepšiu čitateľnosť a odstránenie opakujúceho sa kódu.
Každý skompilovateľný C súbor musí obsahovať funkciu <code>main</code>. Je to funkcia, ktorá sa pri spustení programu v terminály zavolá vždy ako prvá - je to hlavný vstupný bod do programu. Táto funkcia môže potom volať ďalšie funkcie ktoré boli predtým definované, čo nám umožňuje logiku rozkúskovať na viaceré časti, pre lepšiu čitateľnosť a odstránenie opakujúceho sa kódu.


=== Direktíva <code>#include</code> ===
=== Hlavičkové súbory ===
Jednou zo špecifických vlastností jazyka C (a taktiež dôvod, prečo je taký rýchly) je fakt, že základná výbava neobsahuje skoro nič, dokonca ani <code>printf</code>, čo je v mnohých iných jazykoch samozrejmosťou). Tieto funkcie musíme v našom programe zahrnúť pomocou direktívy preprocesora <code>#include</code>, ktorý obsahuje cestu k hlavičkovému súboru s deklaráciami funkcií ktoré zahŕňame (špicaté zátvorky hovoria, že kompilátor má hľadať hlavičkový súbor v systémových cestách, napr.: <code>#include <stdio.h></code>).
Jednou zo špecifických vlastností jazyka C (a taktiež dôvod, prečo je taký rýchly) je fakt, že základná výbava neobsahuje skoro nič, dokonca ani <code>printf</code>, čo je v mnohých iných jazykoch samozrejmosťou). Tieto funkcie musíme v našom programe zahrnúť pomocou direktívy preprocesora <code>#include</code>, ktorý obsahuje cestu k hlavičkovému súboru s deklaráciami funkcií ktoré zahŕňame (špicaté zátvorky hovoria, že kompilátor má hľadať hlavičkový súbor v systémových cestách, napr.: <code>#include <stdio.h></code>).


Riadok 148: Riadok 148:


=== Zdrojový kód ===
=== Zdrojový kód ===
Nedá sa o tom veľa povedať, je to jednoducho C kód, ktorý napíše programátor a uloží do súboru ktorý má obvykle príponu <code>.c</code>.
Je to jednoducho C kód, ktorý napíše programátor a uloží do súboru ktorý má obvykle príponu <code>.c</code>.
 
Každý riadok príkazu musí obsahovať na konci bodkočiarku (<code>;</code>), aby kompilátor vedel kedy príkaz končí - s výnimkou direktív preprocesora (začínajúcich s mriežkou, <code>#</code> ), tieto nemusia mať na konci bodkočiarku. Bloky (scopes) sa definujú pomocou množinových zátvoriek (<code>{</code>, <code>}</code>), za ktorými taktiež nemusí nasledovať bodkočiarka.


=== Preprocesor ===
=== Preprocesor ===
Riadok 156: Riadok 158:


* <code>#include</code> - vloží obsah iného súboru do aktuálneho súboru;
* <code>#include</code> - vloží obsah iného súboru do aktuálneho súboru;
** špicaté (<code><</code>, <code>></code>) zátvorky hovoria, že chceme zahrnúť hlavičkový súbor ktorý sa nachádza v systémových cestách (jedná sa o systémové knižnice ktoré sa nainštalovali spolu s kompilátorom)
** úvodzovky (<code>""</code>) hovoria, že chceme zahrnúť súbor ktorý sa nachádza v ceste relatívne od aktuálneho adresára (teda adresára, kde sa nachádza súbor ktorý kompilujeme)
** napr.: <syntaxhighlight lang="c" line="1">
** napr.: <syntaxhighlight lang="c" line="1">
#include <nazov_suboru.h>  // Systémový súbor
#include <nazov_suboru.h>  // Systémový súbor
Riadok 221: Riadok 225:
== Referencie ==
== Referencie ==
[[Kategória:Programovací jazyk C]]
[[Kategória:Programovací jazyk C]]
<references />{{Téma|Oblast=Kategória:Programovací jazyk C|Poradie=10}}
<references />{{Kvíz}}{{Téma|Oblast=Kategória:Programovací jazyk C|Poradie=10}}

Aktuálna revízia z 09:19, 1. november 2024

Vysvetlíme si ako funguje programovací jazyk C, v čom sa líši od ostatných programovacích jazykov a aké má využitie. Obsahuje taktiež návod pre inštaláciu kompilátorov pre Windows a Mac.


Charakteristika

Programovací jazyk C je jednoznačne jedným z najpopulárnejších jazykov pre nízkoúrovňové programovanie počítačového softvéru. Má široké uplatnenie pre vývoj aplikácií s grafickým používateľským rozhraním, tvorbu hier, operačných systémov, programovanie logických obvodov alebo dokonca tvorbu iných programovacích jazykov vyššej úrovne (napríklad, štandardný interpreter programovacieho jazyku Python je naprogramovaný v C).

V skratke, C sa používa v situáciách keď potrebujeme mať nad nejakým softvérom čo najväčšiu kontrolu (na najnižšej úrovni) a s najvyššou výpočtovou rýchlosťou, s tým že kód zostane pre programátora ľahko čitateľný (napríklad v porovnaní s jazykom Assembly).

Kompilátor

Pre prácu s jazykom C je potrebné nainštalovať kompilátor. Neexistuje štandardný kompilátor ktorý jazyk C poskytuje, preto je potrebné stiahnuť kompilátor od tretej strany. Najčastejšie sa používajú kompilátory GCC a CLang (pre Linux a Mac OS) a MinGW-w64 (pre Windows)[1].

Kompilátor potom môžeme nastaviť v našom integrovanom vývojovom prostredí, napr. Visual Studio Code, a spúšťať aplikácie priamo tam.

Inštalácia MinGW-w64 (pre 64-bitový operačný systém Windows)

Nasledujúci postup bol adaptovaný z dokumentácie pre Visual Studio Code.

Pre inštaláciu GCC kompilátora použijeme softvér MSYS2, čo je sada nástrojov pre vývoj softvéru ktorý beží na operačnom systéme Windows. Pre nás je užitočný manažér balíkov Pacman, ktorý originálne pochádza od Arch Linuxu (poznámka: nejedná sa o slávnu počítačovú hru). Pre stiahnutie MSYS2 inštalátora klikni tu.

Po stiahnutí a otvorení inštalátora softvér nainštalujeme a spustíme. Otvorí sa nám príkazový riadok, do ktorého vložíme nasledovný príkaz pre nainštalovanie MinGW-w64:

pacman -S --needed base-devel mingw-w64-ucrt-x86_64-toolchain

Stlačíme "Enter". Po výzve, aby sme uskutočnili výber balíkov ktoré sa majú nainštalovať potvrdíme predvolený výber stlačením klávesy "Enter":

Okno terminálu ktoré zobrazuje výstup príkazu pre inštalovanie MinGW-w64 prostredníctvom Pacman.
Inštalácia MinGW-w64 prostredníctvom Pacman pre Windows.[2]


Posledný krát sa nás terminál spýta, či naozaj chceme nainštalovať vybrané balíky. Na klávesnici napíšeme písmeno "Y" a potvrdíme tlačením "Enter". Náš C kompilátor by sa mal konečne úspešne nainštalovať.

Avšak, predtým ako môžeme začať kompilátor používať pre vývoj C programov, musíme pridať jeho binárne súbory do systémovej premennej PATH, aby ich našiel systémový terminál.

Úprava premennej PATH v systéme Windows.

Do vyhľadávacieho poľa systému Windows zadáme "systémové premenné" (ak máme nastavený iný jazyk ako slovenčinu, nastavenie bude pod iným menom).

Následne sa otvorí okno ktoré bude v dolnej časti obsahovať tlačidlo "Premenné prostredia...". Kliknutím na tlačidlo sa otvorí ďalšie okno, ktoré bude v hornej časti zobrazovať premenné pre aktuálneho používateľa, a v časti nižšie budú premenné pre systém.

V hornej časti vyberieme v zozname premennú "Path" a klikneme na tlačidlo "Upraviť". Následne môžeme pridať novú položku kliknutím na tlačidlo "Nové". Ako obsah použijeme priečinok bin v inštalačnej ceste MinGW-w64 (predvolene to je C:\msys64\ucrt64\bin).

Pre overenie správnosti nastavenia otvoríme príkazový riadok:

Otvorenie príkazového riadku v systéme Windows.

Vložíme doň nasledovné príkazy:

gcc --version
g++ --version
gdb --version

Pokiaľ bude výstup podobný tomuto, nastavenia sú správne a kompilátor je možné používať:

Príkazy pre kontrolu verzií C kompilátorov MinGW-w64.

Pokiaľ vidíme namiesto výstupu vyššie nejakú chybu, odpoveď sa môže nachádzať v originálnom návode pre inštaláciu, ktorý som upravil a preložil pre potreby tejto stránky.

Ak všetko funguje, môžeme skúsiť napísať náš prvý C program vo Visual Studio Code.

Prvý program C vo VS Code

Spustíme VS Code, vytvoríme nový projekt a nový súbor s názvom main.c (môžeme zvoliť ľubovoľný názov súboru, ak obsahuje správne definovanú funkciu main, spustí sa). Vložíme nasledovný kód, ktorý spustíme (predvolená možnosť je debugovanie, my cez šípku zvolíme iba možnosť "Run"):

#include <stdio.h>

int main() {
    printf("ahoj");
    return 0;
}
Prvý C program a jeho spustenie vo VS Code.


Pri spustení prvého programu nás VS Code vyzve pre výber nášho C kompilátora, čo potvrdíme:

Výber kompilátora pre C vo VS Code.

Ak všetko prebehlo správne, mal by sa v spodnej časti zobraziť terminál s nasledujúcim výstupom (v závislosti od rýchlosti počítača môže kompilácia a samotné spustenie trvať dlhšie):

Výstup prvého programu v C vo VS Code.

Náš prvý program funguje správne a vypíše do terminálu text "ahoj".

Čo ak sa program automaticky nespustí?

Ak sa na terminál nevypíše nič, pravdepodobne sa vytvoril iba build nášho programu (stáva sa to napríklad na Mac OS). V tom prípade musíme zavolať súbor cez terminál manuálne - po úspešnej kompilácií sa vytvorí v aktuálnom adresáry súbor main alebo main.exe (v závislosti od operačného systému). Vložením tejto cesty do terminálu program spustíme (napr.: napíšeme do terminálu ./main alebo ./main.exe).

Štruktúra C programu

Teraz keď máme vytvorený základný program, môžeme si vysvetliť ako to funguje. Základnou jednotkou C programu je funkcia. Programovací jazyk C neobsahuje triedy, všetka logika je preto procedurálna - to znamená, že používame funkcie pre hierarchické vykonávanie blokov príkazov a definovanie logiky nášho programu.

Každý skompilovateľný C súbor musí obsahovať funkciu main. Je to funkcia, ktorá sa pri spustení programu v terminály zavolá vždy ako prvá - je to hlavný vstupný bod do programu. Táto funkcia môže potom volať ďalšie funkcie ktoré boli predtým definované, čo nám umožňuje logiku rozkúskovať na viaceré časti, pre lepšiu čitateľnosť a odstránenie opakujúceho sa kódu.

Hlavičkové súbory

Jednou zo špecifických vlastností jazyka C (a taktiež dôvod, prečo je taký rýchly) je fakt, že základná výbava neobsahuje skoro nič, dokonca ani printf, čo je v mnohých iných jazykoch samozrejmosťou). Tieto funkcie musíme v našom programe zahrnúť pomocou direktívy preprocesora #include, ktorý obsahuje cestu k hlavičkovému súboru s deklaráciami funkcií ktoré zahŕňame (špicaté zátvorky hovoria, že kompilátor má hľadať hlavičkový súbor v systémových cestách, napr.: #include <stdio.h>).

Direktíva #include nefunguje rovnako ako tradičné importy v iných programovacích jazykoch (kde presúvame menný priestor jedného súboru do druhého, a sprístupníme tak definície ktoré obsahuje). Rozdiel je v tom, že #include prikáže preprocesoru skopírovať celý obsah súboru ktorý zahŕňame a prilepiť ho namiesto #include - doslova akoby sme manuálne skopírovali obsah jedného súboru a vložili ho do iného. O túto úpravu, ktorá sa vykoná pred samotnou kompiláciou programu (takzvané predspracovanie zdrojového kódu), sa stará preprocesor (viac o ňom nižšie).

Medzi .h (hlavičkovými) a .c (zdrojovými) súbormi nie je funkcionálne odlišný význam (v zmysle, že oba súbory obsahujú platný C kód), preto táto direktíva vo všeobecnosti funguje aj pre zahrnutie .c súborov (avšak niekedy to môže závisieť od druhu C kompilátora - niektoré povoľujú explicitne iba vkladanie hlavičkových súborov). Z hľadiska konvencie by hlavičkové (.h) súbory mali obsahovať iba deklarácie funkcií alebo premenných, ktoré môžu byť vložené a používané vo viacerých zdrojových (.c) súboroch (nemali by obsahovať samotné implementácie konkrétneho kódu, ktorý kompilujeme).

Samozrejme, v našom prípade nám iba stačí vedieť, že direktívy #include píšeme na začiatku nášho zdrojového kódu (tak, ako tradičné importy v iných programovacích jazykoch) a že nám do kódu vložia funkcionality, ktoré by inak neboli dostupné. Konkrétne prehľady najpoužívanejších hlavičkových súborov sa nachádzajú v tabuľke:

Hlavičkový súbor Direktíva Čo pridáva?
Standard I/O #include <stdio.h> Funkcie pre vstupné a výstupné operácie (scanf, printf, fgets, fputc, a podobne).
Standard library #include <stdlib.h> Všeobecne užitočné veci:
  • funkcie pre správu pamäte (malloc, free, a podobne);
  • funkcie pre generovanie náhodných čísel (rand, srand);
  • funkcie pre kontrolu nad procesom programu (exit, system);
  • funkcie pre konverziu dátových typov (atoi, atof);
String #include <string.h> Funkcie pre manipulovanie s reťazcami (poľami znakov), ako napríklad strcpy, strlen, strcat, strcmp a operácie s pamäťou (memcpy, memset).
Character type #include <ctype.h> Funkcie pre prácu so znakmi (char), napr. islower, isupper, tolower, toupper a podobne.
Mathematics #include <math.h> Matematické funkcie, ako napríklad cos, sin, sqrt, pow, exp, log a podobne.
Time #include <time.h> Funkcie pre prácu s časovými a dátumovými údajmi (time, clock, timediff, strftime a podobne).
File control #include <fcntl.h> Funkcie pre prácu so súbormi (open, read, write a flagy, napríklad O_RDONLY).
Assertions #include <assert.h> Poskytuje makro assert, ktoré sa používa pre kontrolu správnosti údajov a ukončenie programu v prípade, ak sú údaje nesprávne.
Standard bool #include <stdbool.sh> Definuje typ bool s konštantnými hodnotami true alebo false (tieto hodnoty aj tak predstavujú iba štandardnú jednotku a nulu, ale môže sa použiť pre lepšiu čitateľnosť kódu).

Funkcia main

Všetky funkcie v jazyku C sú definované nasledovne:

<návratový typ> <identifikátor funkcie>([parametre]) {
    // príkazy
    return <návratový číselný kód>;
}

Napríklad, funkcia main je definovaná ako:

int main() {
    // nejaký C kód...

    return 0;
}

Táto funkcia sa spustí vždy ako prvá pri spustení skompilovaného programu. Jej návratovou hodnotou je číslo, ktoré určuje či sa program vykonal správne alebo nastala chyba. Vychádza to z unixovej konvencie pre číselné návratové kódy, kde nula znamená že kód zbehol v poriadku. Akákoľvek nenulová hodnota predstavuje chybu - o akú chybu sa jedná, to predstavuje samotné číslo[3].

Pre naše vzdelávacie potreby budeme v 99 % prípadov používať return 0; na konci funkcie main, v zriedkavých prípadoch môžeme použiť return 1; (napríklad ak vyhodnotíme, že používateľ zadal nesprávne údaje na vstupe a program preto nemôže pokračovať, konkrétne napríklad nemôžme sčítať dva reťazce ako číslo). Jazyk C nemá presne definovanú konvenciu pre to, čo znamenajú všetky ostatné návratové čísla - záleží to od programátora alebo platformy na ktorej program beží.

Podľa môjho názoru sa aj tak jedná o archeologický nález z čias, kedy ešte terminál nevidel poriadne textové chybové hlásenia a teda programátori museli v tabuľkách hľadať čo znamenajú jednotlivé číselné kódy ak ich program nepracoval tak ako má - dnes sú už, samozrejme, chybové hlásenia oveľa detailnejšie a preto sa nekladie až taký dôraz na to aby sa týmto číselným kódom priraďoval nejaký pevne stanovený význam v rámci konvencie. Ale terminál nejaký kód dodnes napriek tomu vyžaduje, tak všeobecná konvencia je: "ak je všetko v poriadku, vráť nulu - inak vráť niečo nenulové, zvyčajne jednotku" (ktorá predstavuje všeobecnú chybu, môžeš ju doplniť o textové hlásenie s detailami, ale dôležité je aby terminál vedel že nastala nejaká chyba).

Proces kompilácie C programu

Kompilovanie programu v C predstavuje súbor viacerých krokov, ktoré sú vykonávané chronologicky. Výsledkom tejto činnosti je binárny spustiteľný súbor, ktorý obsahuje strojové inštrukcie pre beh programu.

Zdrojový kód

Je to jednoducho C kód, ktorý napíše programátor a uloží do súboru ktorý má obvykle príponu .c.

Každý riadok príkazu musí obsahovať na konci bodkočiarku (;), aby kompilátor vedel kedy príkaz končí - s výnimkou direktív preprocesora (začínajúcich s mriežkou, # ), tieto nemusia mať na konci bodkočiarku. Bloky (scopes) sa definujú pomocou množinových zátvoriek ({, }), za ktorými taktiež nemusí nasledovať bodkočiarka.

Preprocesor

Ako sme spomenuli vyššie, preprocesor má za úlohu pred kompiláciou vyhľadať v zdrojovom kóde direktívy, ktoré začínajú so znakom mriežky (#), predspracovať tento kód a potom ho v takto spracovanej podobe podať kompilátoru, ktorý skompiluje kód do finálnej podoby.

Preprocesor pozná nasledovné direktívy:

  • #include - vloží obsah iného súboru do aktuálneho súboru;
    • špicaté (<, >) zátvorky hovoria, že chceme zahrnúť hlavičkový súbor ktorý sa nachádza v systémových cestách (jedná sa o systémové knižnice ktoré sa nainštalovali spolu s kompilátorom)
    • úvodzovky ("") hovoria, že chceme zahrnúť súbor ktorý sa nachádza v ceste relatívne od aktuálneho adresára (teda adresára, kde sa nachádza súbor ktorý kompilujeme)
    • napr.:
      #include <nazov_suboru.h>  // Systémový súbor
      #include "nazov_suboru.h"  // Vlastný súbor v aktuálnom adresári
      
  • #define - definuje makrá, ktoré sú v podstate textové náhrady. Môžu mať podobu jednoduchých funkcií alebo konštantných hodnôt.
    • Parametre v tele funkcie by mali obsahovať zátvorky, aj keď sa to z matematického hľadiska v zápise nevyžaduje: dôvodom je to, že ak by sme makro definovali ako #define OBSAH_OBDLZNIKA(a, b) (a * b), a použili ho ako OBSAH_OBDLZNIKA(1 + 2, 2 - 1), tak potom by výsledkom predspracovania bolo 1 + 2 * 2 - 1 a teda priorita operátorov by spôsobila nesprávny výsledok (čo by sa nestalo ak by sme použili klasickú funkciu).
    • Makrá sú iba textové náhrady, neexistujú v pamäti rovnako ako funkcie (taktiež ako v pamäti neexistujú konštantné makrá ako tradičné premenné).
    • napr.:
      #define PI 3.14159                         // Konštantná hodnota
      #define OBSAH_OBDLZNIKA(a, b) ((a) * (b))  // Jednoduchá funkcia s parametrami
      
  • #undef - ruší definíciu makra, čo umožňuje opätovné definovanie makra s rovnakým názvom, ak je to potrebné.
    • napr.:
      #define PI 3.14159
      #undef PI
      #define PI 3.14  // Predefinovanie PI s novou hodnotou
      
  • Podmienené direktívy #ifdef, #ifndef - slúžia pre kontrolu, či sa určitá časť kódu má zahrnúť do programu alebo nie.
    • Užitočné, ak vyvíjame kód pre viacero platforiem kde potrebujeme dynamicky rozhodnúť, ktoré hlavičkové súbory máme zahrnúť (implementácie niektorých funkcií závisia od operačného systému, pretože ich architektúra je iná - napríklad, niečo čo môžeme spraviť vo Windowse nevieme spraviť v Linuxe a naopak, teda musíme dynamicky rozhodnúť ktoré hlavičkové súbory sa majú vložiť).
    • napr.:
      #define DEBUG
      
      #ifdef DEBUG
      printf("túto správu uvidíš iba ak definuješ 'DEBUG'\n");
      #endif
      
      #ifndef DEBUG
      // ladenie nie je povolené
      #endif
      
  • Podmienky #if, #elif a #else - podmienené spracovanie podľa hodnoty výrazu.
    • napr.:
      #define VERSION 2
      
      #if VERSION == 1
      printf("Verzia 1\n");
      #elif VERSION == 2
      printf("Verzia 2\n");
      #else
      printf("Iná verzia\n");
      #endif
      
  • #error - vytvorí chybu počas predspracovania, ak je táto direktíva dosiahnuteľná.
    • napr.:
      #ifndef PI
      #error "PI musí byť definované!"
      #endif
      
  • #pragma - obsahuje pokyny ktoré nie sú v jazyku C štandardizované a sú špecifické pre rôzne kompilátory.
    • napr.:
      #pragma once  // Zabraňuje viacnásobnému zahrnutiu súboru (podobne ako #ifndef ochrany)
      
      #include <stdio.h>  // V poriadku
      #include <stdio.h>  // Toto bude ignorované, obsah súboru nebude vložený dvakrát
      

Preprocesor funguje na princípe "Ctrl + C", "Ctrl + V". Napr.: v prípade #include iba nájde príslušný hlavičkový (alebo zdrojový) súbor ktorý sa nachádza na disku a jeho obsah skopíruje namiesto #include v pôvodnom súbore. Rovnako to funguje aj s makrami a konštantami.

Referencie

Chceš otestovať svoje vedomosti z tejto témy? K dispozícii je kvíz.