Jazyk C

Jazyk C je jedním z nejvýznamnějších imperativních programovacích jazyků, který zásadně ovlivnil vývoj celého odvětví IT. Jazyk C je jednoduchý, expresivní a univerzální, vyznačuje se úsporností a určitým minimalizmem. Není specializovaný pro žádnou určitou oblast a lze jej využít jak k programování mikroprocesorů, tak i k implementaci informačních systémů. Byl poprvé představen v roce 1972 a jeho autory jsou Brian W. Kernighan a Dennis M. Ritchie. Jazyk C se symbioticky vyvíjel společně s Unixem a tato skutečnost je patrná dodnes – jádro i většina programů je napsána právě v C. V průběhu času byl jazyk C několikrát vylepšen a vzniklo i několik standardů, z nichž nejznámnější je standard ANSI 89. Jejich cílem bylo vytvořit „bezespornou a strojově nezávislou definici jazyka C“, což se splnit podařilo. Není tedy chybou považovat jazyk C za multiplatformní.

Jazyk C ideově vychází z jazyků BCPL (Martin Richards) a B (Ken Thompson), ke kterým přidává typy a strojově nezávislou adresovou aritmetiku, realizovanou ukazateli. Každý program v jazyce C se skládá z proměnných a funkcí (neobsahuje objekty). Vstupním bodem programů je speciální funkce main.

Hello world

kód v jazyce C - Zobrazit

  1. #include <stdio.h>
  2.  
  3. int main (void)
  4. {
  5.   puts ("Hello world!");
  6.   return 0;
  7. }

Jednoduchý program, který vypíše řetězec „Hello world!“ na standardní výstup, se skládá z těchto kroků:

  1. Načtení knihovny pro práci se standardním vstupem a výstupem.
  2. Deklarace speciální funkce main, která je vstupním bodem programu.
  3. Výpis řetězce „Hello world!“ na standardní výstup.
  4. Návrat z funkce main s hodnotou 0, která oznamuje, že program skončil úspěšně.

Proměnné

Proměnná je smysluplně pojmenované umístění v paměti počítače. Název proměnné se označuje jako identifikátor. Ten se může skládat z písmen, čísel a podtržítek, začínat ale může pouze podtržítkem nebo písmenem. Klíčová slova nemohou být použita jako identifikátory a délka by neměla přesáhnout „rozumné meze“ (podle implementace 31 až 247 znaků).

Deklarace

Deklarací se rozumí zavedení nové proměnné do zdrojového kódu.

kód v jazyce C - Zobrazit

  1. int number; /* deklarace bez inicializace */
  2. char letter = 'a'; /* deklarace s inicializací */
  3. struct animal cat; /* deklarace strukturované proměnné "klasicky" */
  4. Animal cat; /* deklarace strukturované proměnné "prakticky" */
Ukazatele

Ukazatel (pointer) je speciální primitivní datový typ, který si lze představit jako číslo jednoznačně určující paměťovou buňku. Ukazatel na typ A ukazuje na místo v paměti, kde je uložen typ A. Ukazatel sám o sobě nenese žádná data. Ukazatel se deklaruje pomocí hvězdičky, která se vloží za název datového typu proměnné:

kód v jazyce C - Zobrazit

  1. char * ukazatel;

Adresa na proměnnou se získá použitím operátoru &:

kód v jazyce C - Zobrazit

  1. char znak = 'A';
  2. char * ukazatel = & znak;

Hodnotu v paměti, na kterou ukazuje ukazatel, lze získat tzv. dereferencí, která se zapisuje jako hvězdička před ukazatelem:

kód v jazyce C - Zobrazit

  1. char znak = 'A';
  2. char * ukazatel = & znak;
  3. char kopie = * ukazatel;

Pro přístup k vnitřním datovým hodnotám nějaké struktury lze použít tyto dva ekvivalentní zápisy:

kód v jazyce C - Zobrazit

  1. (* cat).legs = 4;
  2. cat -> legs = 4;
Pole

Pole v jazyku C není nic víc, než ukazatel na svůj první prvek. Jeho velikost se nastavuje při alokaci a za běhu ji nelze měnit (nic však nebrání pole zkopírovat do většího prostoru a dále rozšiřovat).

Alokace pole

Rezervace souvislého paměťového prostoru na žádost programu se nazývá alokace. Během tohoto procesu požádá program operační systém o souvislý blok paměti o velikosti N, kde N je součin velikosti jednoho prvku (v bajtech) a požadovaného počtu prvků. Je-li takový souvislý blok k dispozici, je rezervován pro daný proces a program do něho může ukládat data. Tento prostor je pak třeba před ukončením uvolnit (i když to dnes udělá operační systém za programátora, není vhodné na to spoléhat). Alokace se provádí pomocí funkce malloc a velikost datového typu se zjistí použitím konstruktu sizeof.

Funkce malloc vrací beztypový ukazatel typu void *, takže je nutné jej před přiřazením přetypovat. Po alokaci je více než vhodné zkontrolovat výsledek této operace – může se stát, že počítači dojde paměť a program namísto elegantního ukončení spadne.

kód v jazyce C - Zobrazit

  1. /* alokace dynamické struktury (klasicky) */
  2.  
  3. struct animal * cat = (struct animal *) malloc (sizeof (struct animal));
  4.  
  5. /* alokace dynamické struktury (prakticky) */
  6.  
  7. Animal * cat = (Animal *) malloc (sizeof (Animal));
  8.  
  9. /* alokace pole typu "int" o velikosti 10 prvků */
  10.  
  11. int * arr = (int *) malloc (sizeof (int) * 10);
  12.  
  13. /* velmi doporučeno!!! */
  14.  
  15. if (arr == NULL)
  16. {
  17.   /* alokace neproběhla úspěšně, možná není dost paměti */
  18. }
Dealokace pole

Dealokace paměti se provádí pomocí funkce free. Ihned po dealokaci se doporučuje nastavit ukazateli nějakou „neškodnou“ hodnotu, což nejlépe splňuje hodnota NULL.

kód v jazyce C - Zobrazit

  1. free(arr);
  2.  
  3. /* velmi doporučeno!!! */
  4.  
  5. arr = NULL;

Primitivní datové typy

Název Typ Rozsah Velikost (obvyklá)
(unsigned) char znak 0 až 255 1 bajt
signed char znak –128 až 127 1 bajt
(signed) short int celé číslo –32768 až 32767 2 bajty
unsigned short int celé číslo 0 až 65535 2 bajty
(signed) int celé číslo –2147483648 až 2147483647 4 bajty
unsigned int celé číslo 0 až 4294967295 4 bajty
float reálné číslo +/- 3.4e +/- 38 (cca 7 míst) 4 bajty
double reálné číslo +/- 1.7e +/- 308 (cca 15 míst) 8 bajtů
void obecný typ nemá
A * ukazatel na A nemá 4 bajty

Strukturované datové typy

V jazyce C se nachází i strukturované datové typy, které se dají skládat z primitivních a dalších strukturovaných typů. Tyto datové typy se označují jako struktury (nebo ještě obecněji jako záznamy (records)).

Definice struktury

kód v jazyce C - Zobrazit

  1. /* první možnost (klasická) */
  2.  
  3. struct animal
  4. {
  5.   int eyes;
  6.   int legs;
  7. }
  8.  
  9. /* deklarace proměnné tohoto typu */
  10.  
  11. struct animal cat;

kód v jazyce C - Zobrazit

  1. /* druhá možnost (praktická) */
  2.  
  3. typedef struct animal
  4. {
  5.   int eyes;
  6.   int legs;
  7. } Animal;
  8.  
  9. /* deklarace proměnné tohoto typu */
  10.  
  11. Animal cat;
Přístup k prvkům

kód v jazyce C - Zobrazit

  1. /* zápis do struktury */
  2.  
  3. cat.eyes = 2;
  4. cat.legs = 4;
  5.  
  6. /* čtení ze struktury */
  7.  
  8. printf ("cat has %d eyes", cat.eyes);
  9. printf ("cat has %d legs", cat.legs);

Operátory

Operátor Typ Význam Příklad
+ aritmetický součet dvou čísel a = 1 + 4
aritmetický rozdíl dvou čísel b = 5 – 2
* aritmetický součin dvou čísel c = 5 * 6
/ aritmetický podíl dvou čísel d = 10 / 3
% aritmetický zbytek po dělení dvou čísel e = 12 % 5

Podmínky (if)

kód v jazyce C - Zobrazit

  1. int a = 42;
  2.  
  3. if (a == 0)
  4. {
  5.   puts ("A je nula");
  6. }
  7. else if (a % 2 == 0)
  8. {
  9.   puts ("A je sude");
  10. }
  11. else
  12. {
  13.   puts ("A je liche");
  14. }

Přepínače (switch)

kód v jazyce C - Zobrazit

  1. switch (number)
  2. {
  3.   case 3:
  4.     puts ("Tri orisky pro popelku");
  5.     break;
  6.   case 7:
  7.     puts ("Sedm trpasliku");
  8.     break;
  9.   default:
  10.     puts ("Nic me nenapada...");
  11.     break;
  12. }

Cykly

While

Cyklus „while“ má tvar WHILE (výraz) příkaz. Nejprve se vyhodnotí výraz. Je-li nenulový, provede se tělo cyklu a výraz se znovu vyhodnotí. Takto se pokračuje, dokud je výraz nenulový. Cyklus while se používá zejména tam, kde není nutné provádět před spuštěním cyklu žádnou inicializaci.

kód v jazyce C - Zobrazit

  1. int a = 0;
  2.  
  3. while (a < 10)
  4. {
  5.   printf ("%d", a);
  6.   a = a + 1;
  7. }
Do While

Cyklus „do while“ má tvar DO příkaz WHLE výraz. Nejprve je vykonán příkaz, pak je vyhodnocena výraz. Je-li jeho výsledek nenulový, je příkaz vykonán znovu. Takto se pokračuje, dokud je výraz nenulový.

kód v jazyce C - Zobrazit

  1. int a = 0;
  2.  
  3. do
  4. {
  5.   printf ("%d", a);
  6.   a = a + 1;
  7. }
  8. while (a < 10);
For

Cyklus „for“ má tvar FOR (inicializace, podmínka, operace) příkaz a je zobecněním cyklu while. Používá se především tam, kde je před spuštěním cyklu nutné provést nějakou inicializaci. Tím, že jsou všechny tři řídící výrazy v hlavičce, zvyšuje tento zápis přehlednost. Jeho hlavička obsahuje tři části:

  • inicializaci – co se provede před spuštěním cyklu
  • podmínku – podmínka, která se testuje před každým spuštěním cyklu
  • operaci – příkaz, který se provede na konci každého cyklu

kód v jazyce C - Zobrazit

  1. #include <stdio.h>
  2.  
  3. int main (void)
  4. {
  5.   int f;
  6.  
  7.   for (f = 0; f <= 300; f = f + 20)
  8.   {
  9.     printf ("%d\t%6.1f\n", f, (5.0 / 9.0) * (f - 32));
  10.   }
  11. }

Funkce

Funkcí se rozumí podprogram, který může vracet návratovou hodnotu. Rozdělování kódu na menší celky vede k větší přehlednosti a lepší struktuře zdrojového kódu.

Deklarace

Deklarací se rozumí zavedení nové funkce do zdrojového kódu. Deklarace každé funkce by měla obsahovat návratovou hodnotu, seznam parametrů a jejich typů a identifikátor představující název funkce. Pokud funkce nemá žádné parametry, jako jediný parametr se explicitně uvádí void.

kód v jazyce C - Zobrazit

  1. /* identifikátor této funkce je "max" */
  2. /* návratová hodnota funkce je "int" */
  3. /* funkce má dva parametry: "a", "b" typu "int" */
  4.  
  5. int max (int a, int b)
  6. {
  7.   if (a > b)
  8.   {
  9.     return a;
  10.   }
  11.   else
  12.   {
  13.     return b;
  14.   }
  15. }

kód v jazyce C - Zobrazit

  1. /* identifikátor této funkce je "printLine" */
  2. /* funkce nemá návratovou hodnotu */
  3. /* funkce má jeden parametr "s" typu "int" */
  4.  
  5. void printLine (int s)
  6. {
  7.   int i;
  8.  
  9.   for (i = 0; i < s; i++)
  10.   {
  11.     putchar ('-');
  12.   }
  13.  
  14.   putchar ('\n');
  15. }

Rozdělení do souborů

Zdrojový kód v jazyce C lze pro větší přehlednost rozdělit do více souborů. Každý takový soubor může obsahovat související funkce a proměnné. Každý soubor je kompilován zvlášť, čímž vzniká nespustitelný binární soubor (object file). Aby bylo možné v souborech používat nelokální symboly (proměnné, struktury, funkce, …) z jiných souborů, je nutné získat jejich deklarace. K tomuto účelu vznikají speciální soubory obsahující právě tyto deklarace. Nazývají se hlavičkové soubory (header files) a obvykle končí příponou .h. Před použitím všech symbolů musí být tyto symboly deklarovány.

Příklad

Následující jednoduchý příklad čítače se skládá ze tří souborů:

  • counter.h – hlavičkový soubor obsahující rozhraní čítače
  • counter.c – soubor s implementací čítače
  • main.c – testovací soubor
counter.h

kód v jazyce C - Zobrazit

  1. /* následující makra zabraňují tomu, aby byl obsah hlavičkového souboru vložen dvakrát */
  2.  
  3. #ifndef COUNTER_H
  4. #define COUNTER_H
  5.  
  6. /* hlavička funkce, jejíž implementace se nachází v jiném souboru */
  7.  
  8. void increment (void);
  9. void decrement (void);
  10. int getValue (void);
  11.  
  12. #endif
counter.c

kód v jazyce C - Zobrazit

  1. #include "counter.h"
  2.  
  3. /* statické symboly nejsou nikdy viditelné z jiných souborů */
  4.  
  5. static int counter = 0;
  6.  
  7. static void change (int delta)
  8. {
  9.   counter += delta;
  10. }
  11.  
  12. /* následující symboly jsou viditelné z jiných souborů */
  13.  
  14. void increment (void)
  15. {
  16.   change (1);
  17. }
  18.  
  19. void decrement (void)
  20. {
  21.   change (-1);
  22. }
  23.  
  24. int getValue (void)
  25. {
  26.   return counter;
  27. }
main.c

kód v jazyce C - Zobrazit

  1. /* standardní hlavičkové soubory se uvádí ve špičatých závorkách (nejsou v aktuálním adresáři) */
  2. /* nestandardní (uživatelské) hlavičkové soubory se uvádí v uvozovkách (jsou v aktuálním adresáři) */
  3.  
  4. #include <stdio.h>
  5. #include "counter.h"
  6.  
  7. int main (void)
  8. {
  9.   printf ("Hodnota citace na zacatku: %d\n", getValue ());
  10.   increment ();
  11.   increment ();
  12.   decrement ();
  13.   increment ();
  14.   decrement ();
  15.   increment ();
  16.   increment ();
  17.   decrement ();
  18.   printf ("Hodnota citace na konci: %d\n", getValue ());
  19.  
  20.   return 0;
  21. }

Reference