C (programovací jazyk)

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:
Niektoré vlastnosti, ktoré chýbajú C a nachádzajú sa v ostatných jazykoch:
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&amp;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 codice_3 sa nedoplní. Tieto hlásenia je možné eliminovať niekoľkými malými zmenami originálneho programu:
int main(void)
 printf("hello, world\n");
 return 0;
Nasleduje analýza už uvedeného programu po jednotlivých riadkoch:
Prvý riadok programu je direktíva preprocessora codice_4. 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 codice_5, ktorý obsahuje deklarácie funkcií štandardného vstupu a výstupu. Lomené zátvorky okolo codice_5 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ú codice_7. Funkcia codice_7 má v jazyku C špeciálny význam. Po spustení programu je main prvá funkcia, ktorá sa volá. codice_3 znamená, že "návratová hodnota" – hodnota, na ktorú sa vyhodnotí funkcia codice_7 – je typu integer. codice_11 znamená, že funkcia codice_7 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 codice_7.
 printf("hello, world\n");
Tento riadok "volá" funkciu s názvom codice_14 (odovzdá riadenie jej kódu), ktorá bola deklarovaná vo vloženom hlavičkovom súbore codice_5. Počas tohto volania sa funkcii codice_14 poskytne jediný argument, reťazcový literál codice_17. Jeho časť codice_18 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 codice_14 je typu codice_3, ale volajúci kód ju nepoužíva, a preto sa ignoruje.
 return 0;
Tento riadok ukončuje vykonávanie funkcie codice_7 a priraďuje jej návratovej hodnote nulu.
Táto pravá zložená zátvorka označuje koniec bloku funkcie codice_7.
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:
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 codice_36 pred názvom premennej. Pointer je možné "dereferencovať" (pomocou operátora codice_36), 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 (codice_38). 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 = &lt;náhodná hodnota&gt;
 pA = &lt;náhodná adresa&gt;
Priradíme adresu premennej "A" do premennej "pA". Získanie adresy sa uskutoční operátorom codice_38:
 pA = &amp;A;
Hodnoty premenných budú po tomto kroku nasledovné:
 A = 5
 pA = &lt;adresa premennej A&gt;
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 codice_36.
 B = *pA;
Hodnoty premenných budú po tomto kroku nasledovné:
 A = 5
 pA = &lt;adresa premennej "A"&gt;
 *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 = &lt;adresa premennej "A"&gt;
 *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 codice_34 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[5];
čo definuje pole celých čísel s piatimi prvkami. Prvky poľa sú číslované v intervale formula_1, teda hodnotu prvého a posledného prvku poľa získame volaním:
 int prvyPrvok = pole[0];
 int poslednyPrvok = pole[4];
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:
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.
C sa často používa na nízkoúrovňové systémové programovanie, kde môže byť potrebné zaobchádzať s celým číslom ako s pamäťovou adresou, reálnym číslom s dvojitou presnosťou ako s celočíselným typom alebo s jedným typom pointera ako s iným. Pre tieto prípady C poskytuje "pretypovanie", ktoré vynúti explicitnú typovú konverziu hodnoty z jedného typu na druhý. Použitie pretypovania znamená čiastočné obetovanie typovej bezpečnosti. Pretypovanie sa uskutočňuje uvedením požadovaného typu do okrúhlych zátvoriek pred hodnotu, ktorú chceme pretypovať:
 float desatinneCislo = 5.6F;
 int celeCislo = (int) desatinneCislo;
Mimo explicitných konverzií je prekladač schopný poskytnúť aj tzv. implicitnú typovú konverziu medzi blízkymi typmi. Oproti iným, silnejšie typovaným jazykom ako Java alebo C# je práve pomerne flexibilná implicitná konverzia zdrojom mnohých chýb v kóde. Nasledovný kód je platný, pretože sa uplatňuje implicitná konverzia z rozmerovo menších typov na rozmerovo väčšie:
 double d;
 long l;
 int i;
 if (d &gt; i)
 d = i;
 if (i &gt; l)
 l = i;
 if (d == l)
 d *= 2;
Ukladanie údajov.
Jedna z najdôležitejších funkcií programovacieho jazyka je poskytnutie prostriedkov na správu pamäte a objektov v nej uložených. C poskytuje tri rozdielne spôsoby alokácie objektov:
Tieto tri prístupy majú svoje výhody v rôznych situáciách a majú rôzne nevýhody. Napríklad statická alokácia pamäte nemá žiadne dodatočné náklady na alokáciu (no pri nej sa nedá meniť rozsah alokovanej pamäte), automatická alokácia má malé náklady počas inicializácie a dynamická alokácia má potenciál veľkých nákladov na alokáciu aj uvoľnenie. Na druhej strane priestor zásobníka je zvyčajne oveľa obmedzenejší ako statická pamäť alebo priestor haldy a iba dynamická alokácia umožňuje alokáciu objektov, ktorých veľkosť je známa až počas behu programu. Väčšina programov v C využíva všetky tri spôsoby.
Keď je to možné, preferuje sa zvyčajne automatická alebo statická alokácia, pretože ukladanie spravuje kompilátor, čím sa programátor odbremeňuje od procesu manuálnej alokácie a uvoľňovania pamäte, ktorý je náchylný na chyby. Nanešťastie veľkosť mnohých údajových štruktúr môže počas behu rásť; keďže automatická a statická alokácia vyžadujú známu veľkosť v čase kompilácie, sú mnohé prípady, kedy musí byť použitá dynamická alokácia. Polia s premenlivou veľkosťou sú bežným príkladom (pozri príklad dynamicky alokovaného poľa v článku "malloc").
Syntax.
"Hlavný článok: Syntax jazyka C"
Na rozdiel od jazykov ako Fortran 77 má C voľnú formu, čím umožňuje programátorovi používať tzv. biele znaky (whitespaces) na grafickú úpravu zdrojového kódu. Komentáre je možné vkladať buď uzatvorením medzi codice_49 a codice_50 alebo vložením codice_51 na začiatok riadku (podporované v norme C99).
Každý zdrojový súbor obsahuje deklarácie a definície funkcií. Definície funkcií zasa obsahujú definície premenných a príkazy. Definície premenných buď definujú nové typy pomocou kľúčových slov ako codice_1, codice_2 a codice_31, alebo priraďujú typ a úložný priestor pre nové premenné, zvyčajne napísaním názvu typu nasledovaného názvom premennej. Kľúčové slová ako codice_30 a codice_3, ako aj symbol pointera codice_36, predstavujú vstavané typy. Časti kódu uzatvorené v zložených zátvorkách codice_58 a codice_59 indikujú rozsah, na ktorý sa vzťahujú definície a riadiace štruktúry.
Príkazy vykonávajú činnosti ako zmena hodnoty premennej alebo výstup textu. Riadiace štruktúry slúžia na podmienené alebo cyklické vykonávanie a skladajú sa z kľúčových slov ako codice_60, codice_61, codice_62, codice_63, codice_64, codice_65 a takmer ľubovoľné skoky sú možné použitím codice_66. Činnosť cyklov môžeme ovplyvniť pomocou codice_67 a codice_68. Rôzne vstavané operátory vykonávajú základné aritmetické, logické, porovnávacie, bitové operácie, operácie na indexovanie polí a priradenie. Príkazy tiež môžu volať funkcie, vrátane veľkého množstva štandardných knižničných funkcií vykonávajúcich bežné úlohy.
Príklad programu.
int main(void) {
 for (int i = 0; i &lt; 5; i++) {
 printf("i = %d\n", i);
 return 0;
Každý program v jazyku C musí obsahovať funkciu "codice_7", ktorá vracia celočíselnú hodnotu. Ak program prebehol bez chýb, codice_7 vracia hodnotu 0.
Tento program demonštruje cyklus codice_65. Používa premennú i, ktorá má počiatočnú hodnotu 0 a táto hodnota sa v každom opakovaní cyklu zväčšuje o 1. Na začiatku každého behu cyklu sa testuje podmienka, či i je menšie ako 5. Ak je táto podmienka splnená, telo cyklu prebehne, ináč sa cyklus skončí. Z toho jasne vyplýva, že náš cyklus bude mať 5 opakovaní. Pre názornosť sa zakaždým vypíše aktuálna hodnota premennej i.
Pozn.: deklarácia premennej vnútri codice_72 vyžaduje prekladač s podporou štandardu C99.
Výstup programu:
 i = 0
 i = 1
 i = 2
 i = 3
 i = 4
Problémy.
Populárne príslovie, opakované aj takými významnými návrhármi jazykov ako Bjarne Stroustrup, hovorí, že "C ti uľahčí streliť sa do nohy." Inými slovami, C povoľuje viacero operácií, ktoré sú vo všeobecnosti nežiaduce a tak mnohé jednoduché chyby programátora prejdú kompilátorom a zostanú nepovšimnuté aj počas behu. To vedie k programom s nepredvídateľným správaním a bezpečnostnými dierami.
Dôvodom je čiastočne to, že keď bol C jazyk pôvodne navrhnutý, kontroly počas kompilácie a behu boli príliš drahé. Preto namiesto ich umiestnenia do kompilátora bolo potrebné použiť iné nástroje ako lint. Iným dôvodom je snaha udržať C čo najvýkonnejšie a najflexibilnejšie; čím mocnejší je jazyk, tým ťažšie je dokázať veci o programoch v ňom napísaných. Dnes sú dostupné mnohé nástroje umožňujúce programátorovi detegovať a napraviť rôzne bežné problémy, ale iné je nemožné spoľahlivo detegovať kvôli nedostatku obmedzení C programov.
Jedným z problémov je, že automaticky a dynamicky alokované objekty nie sú inicializované; pred prvým použitím majú ľubovoľnú hodnotu, ktorá sa práve nachádzala v im priradenom pamäťovom priestore. Táto hodnota je do veľkej miery nepredvídateľná a môže byť rôzna na dvoch strojoch, medzi dvomi spusteniami programu alebo medzi dvomi volaniami rovnakej funkcie. Ak sa program pokúsi použiť takúto neinicializovanú hodnotu, výsledok je zvyčajne nepredvídateľný. Väčšina moderných kompilátorov deteguje a varuje pred týmto problémom v určitej obmedzenej množine prípadov.
Pointery sú jedným z hlavných zdrojov nebezpečenstva a pretože sú nekontrolované, je možné, aby pointer ukazoval na objekt ľubovoľného typu vrátane kódu a potom naň mohol zapisovať, čo máva nepredvídateľné následky. Hoci väčšina pointerov ukazuje na bezpečné miesta, to sa môže zmeniť použitím pointerovej aritmetiky, pamäť na ktorú ukazujú môže byť dealokovaná alebo už použitá inde (dangling pointers), môžu byť neinicializované (wild pointers) alebo im môže byť priamo priradená ľubovoľná hodnota použitím pretypovania alebo iného poškodeného pointera. Iný problém s pointermi je, že C umožňuje ľubovoľnú konverziu medzi akýmikoľvek dvomi typmi pointerov. Iné jazyky sa pokúšajú tieto problémy riešiť použitím obmedzujúcejších referenčných typov.
Hoci má C natívnu podporu statických polí, neoveruje, či sú indexy polí platné (bounds checking). Napríklad je možné zapísať do šiesteho prvku poľa s piatimi prvkami, čo má vo všeobecnosti nežiaduce následky. Nazývame to "buffer overflow" (pretečenie buffera) a je to nechvalne známy zdroj množstva bezpečnostných problémov C programov.
Viacrozmerné polia sú potrebné napríklad v numerických algoritmoch (hlavne z aplikovanej lineárnej algebry) na ukladanie matíc (reprezentácií lineárnych zobrazení). Štruktúra poľa v C nie je ani dobre prispôsobená ani vhodná na túto konkrétnu úlohu. Nájdete tam aj riešenie používané v tejto knihe. Je to zároveň slabina aj silné miesto C, že problém existuje a má v rámci jazyka fungujúce riešenie.
Iný bežný problém je, že pamäť haldy nemôže byť znova použitá, kým nie je explicitne uvoľnená programátorom volaním codice_47. Výsledkom je, že ak programátor zabudne uvoľniť pamäť, ale ďalej ju alokuje, bude sa postupne spotrebovávať viac a viac pamäte. Nazývame to "memory leak". Naopak, je možné uvoľniť pamäť príliš skoro a potom pokračovať v jej používaní. Pretože alokačný systém môže kedykoľvek znova použiť túto pamäť, výsledkom býva nepredvídateľné správanie. Tieto problémy riešia jazyky s automatickou garbage collection.
Ďalším bežným problémom sú variadické funkcie, ktoré majú premenlivý počet argumentov. Na rozdiel od funkcií s prototypom nie je kontrola argumentov variadických funkcií počas kompilácie vyžadovaná štandardom. Ak sa poskytne zlý údajový typ, výsledok je nepredvídateľný a často fatálny v dôsledku zničenia obsahu zásobníka. Variadické funkcie tiež obsluhujú konštantu nulového pointera neočakávaným spôsobom. Napríklad rodina funkcií printf poskytovaných štandardnou knižnicou, ktoré sa používajú na tvorbu formátovaného textového výstupu je nechvalne známa svojim variadickým rozhraním náchylným na chyby, ktoré sa spolieha na formátovací reťazec, aby špecifikoval počet a typ nasledujúcich argumentov. Typová kontrola variadických funkcií štandardnej knižnice je však vecou kvality implementácie a mnohé moderné kompilátory robia špeciálne kontroly printf volaní a produkujú varovania ak je zoznam argumentov nekonzistentný s formátovacím reťazcom. Treba poznamenať, že nie všetky volania printf je možné kontrolovať staticky (to je zložité keď samotný formátovací reťazec pochádza z ťažko vystopovateľného miesta), a iné variadické funkcie zvyčajne zostávajú nekontrolované.
Boli vytvorené nástroje, ktoré pomáhajú programátorovi vyhnúť sa mnohým z týchto chýb vo viacerých prípadoch. Automatická kontrola zdrojového kódu a auditácia sa hodí v každom programovacom jazyku a pre C existuje množstvo takýchto nástrojov ako napríklad Lint. Bežnou praxou je použiť Lint na detekciu pochybného kódu hneď po napísaní programu. Keď program prejde Lintom, skompiluje ho aj kompilátor C. Existujú tiež knižnice na kontrolu hraníc polí (bounds checking) a obmedzená forma automatickej garbage collection, ale nie sú štandardnou súčasťou jazyka ani knižníc.
C má aj iné problémy, ktoré priamo neústia do chýb, ale sťažujú programátorovi možnosť budovať robustné a jednoducho spravovateľné systémy. Medzi ne patria:
História.
Skoré začiatky.
Začiatky vývoja C siahajú do AT&amp;T v Bellovych laboratóriách medzi rokmi 1969 a 1973; podľa Ritchieho bol najkreatívnejším obdobím rok 1972. Pomenovanie "C" dostal, pretože mnohé vlastnosti boli odvodené od skoršieho jazyka zvaného "B". 
Názory na pôvod názvu "B" sa líšia: Ken Thompson tvrdí, že zásluha patrí programovaciemu jazyku BCPL, ale vytvoril aj jazyk zvaný Bon na počesť jeho manželky Bonnie.
Ohľadne pôvodu C a súvisiaceho operačného systému Unix koluje veľa legiend, vrátane nasledovnej:
V roku 1973 sa už jazyk C stal dostatočne silný na to, aby väčšina UNIXového kernelu, pôvodne napísaného v assembleri PDP-11/20, mohla byť prepísaná do C. Toto bolo jedno z prvých jadier operačného systému implementovaných v inom jazyku ako assembler, pričom skoršími boli systémy Multics (napísaný v jazyku PL/I), TRIPOS (v BCPL) a MCP (Master Control Program) pre Burroughs B5000 napísaný v ALGOLe v roku 1961.
K&amp;R C.
V roku 1978, Dennis Ritchie a Brian Kernighan publikovali prvé vydanie knihy "The C Programming Language". Táto kniha známa medzi programátormi ako "K&amp;R", slúžila mnoho rokov ako neformálna špecifikácia jazyka. Verzia C, ktorú opisuje sa bežne nazýva „K&amp;R C“. Druhé vydanie knihy pokrýva neskorší, dolu opísaný štandard ANSI C.
K&amp;R zaviedli do jazyka nasledovné prvky:
K&amp;R C sa často považuje za najzákladnejšiu časť jazyka, ktorú má C kompilátor podporovať. Po mnoho rokov, dokonca aj po uvedení ANSI C, bol považovaný za "najmenší spoločný násobok", ktorý C programátori dodržiavali, ak bola potrebná maximálna portabilita, keďže nie všetky kompilátory boli aktualizované na plnú podporu ANSI C a rozumne napísaný K&amp;R C je tiež platným ANSI C.
V týchto skorých verziách C bolo potrebné pred použitím definovať či deklarovať (použitím prototype výrazu) iba funkcie, ktoré nevracali hodnotu typu integer. Funkcia použitá bez akejkoľvek deklarácie bola považovaná za funkciu vracajúcu typ integer. Parametre funkcií nemali typovú kontrolu, hoci niektoré kompilátory vyhlásili varovanie ak bola funkcia zavolaná so zlým počtom argumentov.
V rokoch po publikácii K&amp;R C, bolo pridaných do jazyka niekoľko "neoficiálnych" vlastností, podporovaných kompilátormi od AT&amp;T a niektorými inými. Medzi ne patrili:
ANSI C a ISO C.
Koncom sedemdesiatych rokov začalo C nahrádzať programovací jazyk BASIC ako vedúci programovací jazyk pre mikropočítače. Počas osemdesiatych rokov bol prispôsobený na použitie s IBM PC a jeho popularita výrazne vzrástla. V rovnakom čase Bjarne Stroustrup a iní v Bellovych laboratóriách začali pracovať na pridaní štruktúr objektovo orientovaného programovania do C. Jazyk, ktorý tak vznikol bol nazvaný C++ a dnes je najbežnejším aplikačným programovacím jazykom pre operačný systém Microsoft Windows; C zostáva populárny vo svete Unixu. Iným jazykom vyvinutým v tej dobe bol Objective-C, ktorý tiež pridáva schopnosti OOP. Hoci nie je až taký populárny ako C++, používa sa napríklad na vývoj Cocoa aplikácií pre Mac OS X.
V roku 1983 vytvoril Americký Národný Štandardizačný Inštitút (ANSI) komisiu X3J11 na zostavenie štandardnej špecifikácie C. Po dlhom a únavnom procese bol štandard dokončený v roku 1989 a ratifikovaný ako ANSI X3.159-1989 "Programming Language C". Táto verzia jazyka sa bežne nazýva ANSI C. V roku 1990 bol ANSI C štandard (s niekoľkými drobnými modifikáciami) prijatý Medzinárodnou štandardizačnou Organizáciou (ISO) ako ISO/IEC 9899:1990.
Jedným z cieľov štandardizačného procesu ANSI C bolo vytvoriť nadmnožinu K&amp;R C, ktorá by zahŕňala množstvo následne uvedených neoficiálnych vlastností. Štandardizačná komisia však pridala aj niekoľko nových vlastností ako prototypy funkcií (požičané z C++) a schopnejší preprocessor.
ANSI C je dnes podporovaný takmer všetkými najpoužívanejšími kompilátormi. Väčšina dnes napísaného C kódu je založená na ANSI C. Akýkoľvek program napísaný "iba" v štandardnom C sa zaručene bude správať korektne na akejkoľvek platforme s konformnou implementáciou C. Boli však napísané mnohé programy, ktoré sa korektne skompilujú iba na istej platforme alebo istom kompilátore, z dôvodu :
C99.
Po skončení štandardizačného procesu zostala špecifikácia C jazyka po určitú dobu relatívne nemenná, kým C++ sa ďalej vyvíjal. (Normatívny Dodatok 1 vytvoril novú verziu jazyka C v roku 1995, ale táto verzia sa spomína zriedka). Štandard však podstúpil revíziu na konci deväťdesiatych rokov, čo viedlo k vydaniu ISO 9899:1999 v roku 1999. 
Tento štandard sa bežne nazýva "C99". Bol prijatý za ANSI štandard v marci 2000.
Medzi nové vlastnosti v C99 patria:
Záujem o zahrnutie nových vlastností C99 sa zdá byť rôzny. Kým GCC a niekoľko iných kompilátorov dnes podporuje väčšinu vlastností C99, kompilátory od Microsoftu a Borlandu nie, a tieto spoločnosti neprejavujú záujem o pridanie tejto podpory.
Vzťah k C++.
Programovací jazyk C++ bol pôvodne odvodený od C. Ale na rozdiel od všeobecného názoru nie každý C program je platným C++ programom. Ako sa C a C++ nezávisle vyvíjali, nastal nešťastný nárast nekompatibilít medzi týmito dvomi jazykmi. Najnovšia revízia C, C99, vytvorila niekoľko ďalších konfliktných vlastností. Tieto rozdiely sťažujú písanie programov a knižníc, ktoré je možné korektne skompilovať ako C aj C++ kód, a mätú tých, ktorí programujú v oboch jazykoch. Odlišnosti tiež sťažujú adaptáciu vlastností z jedného jazyka do druhého.
Bjarne Stroustrup, tvorca C++, opakovane navrhoval, aby sa počet nekompatibilít medzi C a C++ zredukoval na najnižšiu možnú mieru. Iní argumentujú, že keďže C a C++ sú dva odlišné jazyky, kompatibilita medzi nimi je užitočná, ale nie životne dôležitá a podľa tohto tábora by snahy redukovať nekompatibilitu nemali zamedzovať pokusom o vylepšenie každého z jazykov jednotlivo.
Dnes sú primárnymi rozdielmi medzi týmito jazykmi:
C99 prijalo niektoré ďalšie vlastnosti, ktoré sa po prvýkrát objavili v C++. Medzi ne patria: