Programovací jazyk C je štandardný programovací jazyk vyvinutý začiatkom 70. rokov 20. storočia. Autorom jazyka je Dennis Ritchie. Pôvodne bol určený pre použitie na operačných systémoch UNIX. Odvtedy sa rozšíril na mnohé iné operačné systémy a je jedným z najpoužívanejších programovacích jazykov. C sa cení vďaka jeho efektívnosti a je najpopulárnejším jazykom na písanie systémového softvéru, hoci sa používa aj na tvorbu aplikačného softvéru. Tiež sa bežne používa pri výuke programovania, hoci nie je určený pre úplných začiatočníkov.

Programovací jazyk C sa od roku 2000 konzistentne nachádza na jednej z prvých dvoch priečok hodnotenia TIOBE index, ktorý porovnáva popularitu programovacích jazykov.

Vlastnosti Prehľad C je pomerne minimalistický tzv. imperatívny programovací jazyk na úrovni blízkej hardvéru a je podobnejší strojovo orientovaným jazykom ako väčšina jazykov vyššej úrovne. O jazyku C sa niekedy hovorí ako o "prenosnom strojovom jazyku" (portable assembler). Na rozdiel od strojovo orientovaných jazykov je kód v jazyku C možné skompilovať (preložiť do strojového kódu) pre takmer každý počítač. Táto vlastnosť spolu s tým, že samotný prekladač jazyka C i väčšina jadra UNIXu boli prepísané do C, uľahčilo ich rozšírenie na rôzne počítače. Z týchto dôvodov nazvali jazyk C jazykom strednej úrovne.

Hlavným dôvodom vytvorenia jazyka C bolo uľahčiť písanie programov s menším množstvom chýb procedurálnym spôsobom, ale bez dodatočnej záťaže na kompilátor, ktorého tvorbu komplikujú komplexné vlastnosti jazyka. Z tohto dôvodu má C nasledujúce dôležité vlastnosti:

Jednoduché jadro jazyka s dôležitou funkcionalitou ako matematické funkcie alebo obsluha súborov presunutou do množiny knižničných rutín namiesto toho, aby bola súčasťou jazyka Zameranie na paradigmu procedurálneho programovania s prvkami pre programovanie štruktúrovaným štýlom Jednoduchý systém typov, ktorý predchádza ich chybnému použitiu Použitie preprocesorového jazyka, C preprocesor, pre úlohy ako definícia makier a viacnásobné vkladanie (include) zdrojového súboru Nízkoúrovňový prístup k pamäti počítača pomocou ukazovateľov (smerníkov) Minimalistická množina kľúčových slov Parametre sa odovzdávajú hodnotou, odovzdávanie parametrov odkazom sa realizuje pomocou mechanizmu ukazovateľov. Ukazovatele na funkcie, ktoré umožňujú zárodočnú formu uzáveru a polymorfizmu Zmena lexikálneho významu premennej podľa miesta výskytu Záznamy, alebo užívateľsky definované zložené údajové typy (struct, union), ktoré umožňujú kombináciu súvisiacich údajov a manipuláciu s nimi ako s celkom Čo najmenej iného

Niektoré vlastnosti, ktoré chýbajú C a nachádzajú sa v ostatných jazykoch: Prísna typová kontrola Automatické čistenie pamäti Triedy alebo objekty (pozri objekovo orientované programovanie) Rozvinutý systém typov Uzávery Vnorené funkcie (hoci kompilátor GCC ich obsahuje ako rozšírenie) Generické programovanie Preťažovanie funkcií a preťažovanie operátorov Metaprogramovanie Prirodzená podpora multithreadingu a sietí. V jazyku C sa na tieto účely používajú knižnice podľa štandardov POSIX (pre multithreading) a implementácie tzv. socketov pre sieťové programovanie. Spracovanie zoznamov Hoci je zoznam vlastností, ktoré jazyku C chýbajú, dlhý, neovplyvnilo to jeho prijatie. Jeho hlavnou výhodou je ľahké písanie kopmpilátorov pre nové platformy a to, že umožňuje programátorovi do veľkej miery riadiť činnosť programu. To často umožňuje efektívnejší beh programu v porovnaní s inými jazykmi. Zvyčajne iba ručne optimalizovaný kód v strojovom jazyku beží rýchlejšie, keďže má úplný prístup k strojovému vybaveniu, ale pokroky v optimalizácii kompilátorov spolu s komplexnosťou moderných procesorov zúžili tento rozdiel.

Jeden z dôsledkov širokého prijatia a efektívnosti jazyka C je, že kompilátory, knižnice a interpretery iných vysokoúrovňových jazykov sú často implementované v jazyku C.

Príklad „hello, world“

Nasledujúca jednoduchá aplikácia sa objavila v prvom vydaní K&R, a stala sa štandardným úvodným programom väčšiny kníh zaoberajúcich sa výučbou programovania, nezávisle od použitého jazyka. Program vypíše „hello, world“ na štandardný výstup, čo je zvyčajne terminál alebo obrazovka počítača. Môže to však byť aj súbor alebo nejaké iné hardvérové zariadenie, v závislosti od toho, ako je mapovaný štandardný výstup v čase vykonania programu.

main() { printf("hello, world\n"); }

Hore uvedený program sa korektne skompiluje na väčšine moderných kompilátorov, ktoré nie sú v kompatibilnom režime. Kompilátory podľa štandardu ANSI C však vypíšu niekoľko varovných hlásení. Navyše sa kód neskompiluje, ak kompilátor striktne dodržiava štandard C99, keďže návratová hodnota typu int sa nedoplní. Tieto hlásenia je možné eliminovať niekoľkými malými zmenami originálneho programu:

#include <stdio.h>

int main(void) { printf("hello, world\n"); return 0; }

Nasleduje analýza už uvedeného programu po jednotlivých riadkoch:

#include <stdio.h> Prvý riadok programu je direktíva preprocessora #include. Hovorí preprocesoru – prvému nástroju, ktorým prechádza zdrojový kód počas kompilácie – aby nahradil uvedený riadok celým textom súboru alebo inou entitou, na ktorú odkazuje. V tomto prípade sa riadok nahradí hlavičkovým súborom stdio.h, ktorý obsahuje deklarácie funkcií štandardného vstupu a výstupu. Lomené zátvorky okolo stdio.h naznačujú, že súbor je možné nájsť na jednom z miest, ktorých zoznam preprocesoru dávame pomocou search path pre hlavičkové súbory.

int main(void) Tento ďalší riadok hovorí, že definujeme funkciu zvanú main. Funkcia main má v jazyku C špeciálny význam. Po spustení programu je main prvá funkcia, ktorá sa volá. int znamená, že návratová hodnota – hodnota, na ktorú sa vyhodnotí funkcia main – je typu integer. (void) znamená, že funkcia main neočakáva argumenty od kódu, ktorý ju volá. Pozri aj void.

{ Táto ľavá zložená zátvorka označuje začiatok bloku funkcie main.

printf("hello, world\n"); Tento riadok volá funkciu s názvom printf (odovzdá riadenie jej kódu), ktorá bola deklarovaná vo vloženom hlavičkovom súbore stdio.h. Počas tohto volania sa funkcii printf poskytne jediný argument, reťazcový literál "hello, world\n". Jeho časť \n je tzv. escape sekvencia, ktorá sa preloží na znak EOL, end-of-line, ktorý posunie kurzor v termináli na začiatok nasledujúceho riadku. Návratová hodnota funkcie printf je typu int, ale volajúci kód ju nepoužíva, a preto sa ignoruje.

return 0; Tento riadok ukončuje vykonávanie funkcie main a priraďuje jej návratovej hodnote nulu.

} Táto pravá zložená zátvorka označuje koniec bloku funkcie main.

Typy C má systém typov podobný iným nasledovníkom ALGOLu ako Pascal, hoci sa odlišuje niekoľkými spôsobmi. Existujú typy pre:

celé čísla rôznych veľkostí (int, short, long), pre každú veľkosť typ so znamienkom (signed) a bez znamienka (unsigned) čísla s plávajúcou desatinnou čiarkou s jednoduchou (float) a dvojitou (double) presnosťou znaky (char) vymenované typy (enum) záznamy (struct) únie (union) špeciálny prázdny typ (void), ktorý je možné použiť na indikáciu, že funkcia nevracia hodnotu, respektíve nemá žiadne parametre a na označenie ukazovateľa na ľubovoľný typ (void *).

Ukazovatele (smerníky, pointery)

C veľmi často využíva smerníky (ukazovatele, pointery), veľmi jednoduchý typ odkazu, ktorý ukladá adresu pamäťového miesta. Premenná, ktorá reprezentuje pointer na hodnotu sa deklaruje so znakom * pred názvom premennej. Pointer je možné dereferencovať (pomocou operátora *), aby sa sprístupnili údaje uložené na danej adrese. Adresu je možné meniť bežným priradením a aritmetikou ukazovateľov.

Adresu ľubovoľnej premennej (ale aj funkcie) je možné získať použitím operátora získania adresy (&). Výsledkom je pointer na typ premennej s hodnotou adresy premennej, v prípade funkcií je to adresa funkcie. Príklad zaobchádzania s pointermi:

Definujeme celočíselné premenné s názvami A a B: int A, B; Nastavíme hodnotu premennej A na hodnotu 5: A = 5; Definujeme premnennú s názvom pA typu pointer na celočíselnú premennú: int *pA; Hodnoty premenných budú po tomto kroku nasledovné: A = 5 B = <náhodná hodnota> pA = <náhodná adresa> Priradíme adresu premennej A do premennej pA. Získanie adresy sa uskutoční operátorom &: pA = &A; Hodnoty premenných budú po tomto kroku nasledovné: A = 5 pA = <adresa premennej A> Priradíme hodnotu na ktorú odkazuje premenná pA do premennej B. Keďže premenná pA obsahuje ADRESU premennej A, musíme získať hodnotu na tejto adrese. To docielime dereferencovaním adresy uloženej v premennej pA na hodnotu pomocou operátora *.

B = *pA; Hodnoty premenných budú po tomto kroku nasledovné: A = 5 pA = <adresa premennej ''A''> *pA = 5 B = 5 Zmeníme hodnotu premennej A na hodnotu 10: A = 10; Keďže premenná pA ukazuje na premennú A, táto zmena sa prejaví aj pri dereferencovaní premennej pA. Premenná B však obsahuje priamo hodnotu 5, teda táto zmena sa jej nedotkne: A = 10 pA = <adresa premennej ''A''> *pA = 10 B = 5

Počas behu programu, ukazovateľ predstavuje pamäťovú adresu. V čase kompilácie je to zložený typ reprezentujúci adresu aj typ údajov. To umožňuje typovú kontrolu pointerov. Pointery sa v jazyku C používajú na rozličné účely, napríklad textové reťazce s premenlivou dĺžkou sú bežne reprezentované pointerom na prvý znak reťazca. Dynamická alokácia pamäte, ktorá je popísaná nižšie, sa uskutočňuje pomocou pointerov.

Null pointer je vyhradená hodnota, ktorá neukazuje na platné pamäťové miesto (respektíve ukazuje na neexistujúce miesto v pamäti). Je užitočná na indikáciu špeciálnych prípadov ako pointer na ďalší uzol posledného uzla spojového zoznamu. Dereferencovanie null pointera spôsobuje nepredvídateľné správanie (najčastejšie chybu programu a jeho ukončnie). Pointery na typ void tiež existujú a ukazujú na objekty neznámeho typu. To je obzvlášť užitočné pri generickom programovaní . Keďže veľkosť a typ objektov, na ktoré ukazujú nie je známy, nie je možné ich dereferencovať, ale je možné ich konvertovať na iné typy pointerov pomocou pretypovania.

Polia

Polia v jazyku C sú pevnej statickej veľkosti známej v čase kompilácie; v praxi to nespôsobuje žiadne nedostatky vďaka tomu, že je možné alokovať bloky pamäte počas behu programu použitím štandardnej knižnice a zaobchádzať s nimi ako s poľami. Polia sa definujú nasledovne: int pole;

čo definuje pole celých čísel s piatimi prvkami. Prvky poľa sú číslované v intervale , teda hodnotu prvého a posledného prvku poľa získame volaním: int prvyPrvok = pole; int poslednyPrvok = pole;

C pri kompilácii nekontroluje hranice polí, teda je možné napísať kód, ktorý sa odkazuje za pole a bude preložiteľný kompilátorom. Pri behu programu sa však s najväčšou pravdepodobnosťou vyskytne chyba adresácie pamäte (Segmentation fault).

S poľom môžeme previesť iba dve operácie:

Zistiť jeho veľkosť (pomocou operátora sizeof) v bajtoch. Získať jeho adresu (pomocou operátora &)

Všetky ďalšie operácie ako nastavenie a získanie hodnoty sa potom realizujú ako operácie s ukazovateľom na začiatok poľa a aritmetikou ukazovateľov. Bližšie pozri kapitolu Rozdiel medzi poľami a pointermi.

C tiež poskytuje viacrozmerné polia. Sémanticky tieto polia fungujú ako polia polí, ale fyzicky sú uložené ako jednorozmerné pole s offsetmi, ktoré sa počítajú. Pole je teda uložené v pamäti zaradom, pričom ako prvé sa menia indexy od najvyššieho rozmeru (radenie podľa riadkov).

Pretypovanie