Vybrané odstavce z teorie jazyka C

Procedura vytvoření programu

   |
   | TEXTOVY EDITOR
   |
   V
1. zdrojak.c
   |
   | PREPROCESOR (cpp)
   |
   V
2. upraveny text
   |
   | PREKLADAC (COMPILER gcc)
   |
   V
3. prelozeny soubor
   |
   | SESTAVOVACI PROGRAM (LINKER ld)
   |
   V
4. spustitelny program

     DEBUGGER (gdb)

1.-> 2.  gcc zdrojak.c -E
1.-> 3.  gcc -Wall zdrojak.c -c
1.-> 4.  gcc -Wall zdrojak.c -o program

Preprocesor, makra

Preprocesor
- preproces se provádí před překladem - odstraňuje komentáře ze zdrojových textů - vkládá do zdrojových textů další - většinou hlavičkové - soubory - provádí zpracování maker (textové nahrazení makra) - připravuje zdrojové texty pro podmíněný překlad
Některé instrukce preprocesoru
#include <soubor.h> // vložení souboru umístěného v systémovém adresáři #include "cesta/soubor.h" // vložení souboru umístěného v uživatelském adresáři #define POCET 20 // vytvoření makra bez parametru - tzv. symbolické konstanty #define ALOKUJ (POCET+1) // - pokud se v rozvoji makra vyskytuje výraz, MUSÍ být v závorce #define HELP " Program xyz dělá spoustu užitečných věcí\n\ Syntax: xyz [options] arg1 arg2 ...\n\ význam argumentů: ... ... ...\n\n" // - textový rozvoj může být i složitější #define CHYBA { printf("Chyba - neplatné argumenty !\n"); return 1; } // - textový rozvoj může být i příkaz (blok příkazů) jazyka C #define sqr(x) ((x)*(x)) // makro s parametrem - každý výskyt parametru v rozvoji MUSÍ být v závorce #define prohod(x,y) { int pom; pom=x; x=y; y=pom; } // výjimka potvrzuje pravidlo #ifdef symbol // případně #ifndef symbol ... #else // nepovinné ... #endif // ze zdrojáku se vypustí část nevyhovující podmínce
Doporučený obsah hlavičkového souboru
- komentář popisující soubor - funkční prototypy - makra bez parametrů - symbolické konstanty - makra s parametry - definice typů - definice jednoznačného symbolu a instrukce zabraňující vícenásobnému include x nedefinujte tu proměnné x omezujte další výskyt instrukce #include

Formátovací řetězec

obsahuje:
  normální znaky
  "escape" znaky        ...  \n \a \b \r \t \\ \osmickove_cislo
  formátovací direktivy ...  %[příznaky][šířka][.přesnost][modifikátor]konverze

příznaky
    +  ...  vždy bude uvedeno znaménko (i u kladného čísla)
    -  ...  zarovnání doleva
   ' ' ...  mezera před kladným číslem
    '  ...  vloží oddělovače tisíců (podle nastavení národního prostředí)
šířka
    n  ...  výpis na minimálně n pozic, zleva se doplní mezery (n je celé číslo)
   0n  ...  výpis na minimálně n pozic, zleva se doplní nuly
    *  ...  počet pozic je dán dalším argumentem funkce printf(), který předchází vypisovaný výraz
přesnost
    pro celé číslo    ...  šířka
    pro reálné číslo  ...  počet cifer za desetinnou tečkou
    pro řetězec       ...  maximální počet pozic - tj. ořízne řetězec
    *  ...  počet desetinných míst je dán dalším argumentem funkce printf(), který předchází vypisovaný výraz
modifikátor
    hh ...  modifikuje celočíselné konverze na typ (un)signed char
    h  ...  modifikuje celočíselné konverze na typ (un)signed short int
    l  ...  modifikuje celočíselné konverze na typ (un)signed long int
    ll ...  modifikuje celočíselné konverze na typ (un)signed long long int
    L  ...  modifikuje reálné konverze na typ long double
konverze
  i,d  ...  desítkové číslo typu signed int
    u  ...  desítkové číslo typu unsigned int
    o  ...  osmičkové číslo typu unsigned int
    x  ...  šestnáctkové číslo typu unsigned int , cifry abcdef
    X  ...  šestnáctkové číslo typu unsigned int , cifry ABCDEF

  f,F  ...  reálné (double) číslo v desetinném tvaru (12.123456), implicitní přesnost = 6
    e  ...  double číslo v semilogaritmickém tvaru (1.234e5)
    E  ...  double číslo v semilogaritmickém tvaru (1.234E5)
    g  ...  double číslo - podle hodnoty a přesnosti se zvolí desetinný nebo semilog. tvar (e) 
    G  ...  double číslo - podle hodnoty a přesnosti se zvolí desetinný nebo semilog. tvar (E)

    c  ...  znak
    s  ...  řetězec
    p  ...  pointer
    %  ...  znak %

Datové typy

jednoduché
celé číslo reálné číslo znak ukazatel výčtový typ
strukturované
pole strukrura (~záznam) union (~variantní záznam) bitové pole (~~množina)

Číselné datové typy

Celá čísla:
základní typ int lze ovlivnit modifikátory short,long,signed,unsigned (signed je implicitní) pokud je nějaký modifikátor uveden, pak se může vynechat slovo int typ velikost rozsah int 4B -2 147 483 648 .. 2 147 483 647 unsigned int 4B 0 .. 4 294 967 295 short 2B -32 768 .. 32 767 unsigned short 2B 0 .. 65 535 long 4B -2 147 483 648 .. 2 147 483 647 unsigned long 4B 0 .. 4 294 967 295 long long 8B -9 223 372 036 854 775 808 .. 9 223 372 036 854 775 807 unsigned long long 8B 0 .. 18 446 744 073 709 551 615 (tj. 1,8.1019) char 1B -128 .. 127 unsigned char 1B 0 .. 255
Reálná čísla:
max. ... maximální zobrazitelné číslo min. ... minimální epsilon ... nejmenší číslo, pro nějž platí: 1.0+epsilon > 1.0 přesnost ... počet platných desítkových číslic mantisy typ velikost max. min. epsilon přesnost float 4B 3.402e+38 1.175e-38 1.192e-07 6 double 8B 1.797e+308 2.225e-308 2.220e-16 15 long double 12B 1.189e+4932 3.362e-4932 1.084e-19 18

Operátory

Speciální unární
++ ... inkrementační -- ... dekrementační
Aritmetické
+ ... sčítání - ... odčítání * ... násobení / ... dělení - celočíselné, pokud jsou oba operandy calá čísla, jinak reálné % ... zbytek po celočíselném dělení
Přiřazovací
= ... normální přiřazovací += ... např. x = x + a; lze zkrátit x += a; -= ... x = x - a; x -= a; *= ... x = x * a; x *= a; /= ... x = x / a; x /= a; %= ... x = x % a; x %= a;
Relační
== ... rovno != ... nerovno > ... větší než >= ... větší nebo rovno < ... menší než <= ... menší nebo rovno
Logické
&& ... a zároveň || ... nebo ! ... negace
Bitové
& ... and | ... or ^ ... xor >> ... bitový posuv vpravo << ... bitový posuv vlevo ~ ... bitová negace (unární operátor)

Typová konverze

implicitní
- samostatné operandy typů: char,short --> int (nebo unsigned int) ( float --> double ... pouze v některých definicích jazyka C ) - 2 operandy různých typů: int --> unsigned int --> long --> unsigned long --> float --> double --> long double - přiřazení: typ výrazu konvertuje na typ proměnné, do které se přiřazuje
explicitní
(typ) výraz

Příkazy

jednoduché
volání funkce přiřazovací skok (return,break,continue,goto) prázdný
strukturované
složený podmínkový (if) větvení (switch) cyklu (for,while,do)

Generování (pseudo)náhodných čísel

int rand(void)			... funkce vrací celé číslo z intervalu     0 ... RAND_MAX
rand() % 100			... celočíselný výraz s hodnotou v rozmezí  0 ... 99
rand() / (RAND_MAX + 1.0)	... reálný výraz s hodnotou z intervalu   < 0 , 1 )

void srand(unsigned int seed)	... funkce inicializuje generátor náhodných čísel
					hodnota parametru seed určuje, jaká řada čísel bude generována

Symbolická konstanta RAND_MAX je definována
a funkce srand(), rand() mají hlavičky uvedeny v souboru stdlib.h

Příklad:
--------
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int i;

int main(void) {
  srand(time(NULL));					// time() vraci pocet sekund od 1.1.1970
  for (i=0;i<=100;i++) printf("%4d", rand()%100);
  printf("\n");
  return 0;
}


Druhy ukazatelů, paměťové oblasti


ukazatel - netypový (void *) ... obsahuje adresu bez dalších informací
         - typový (typ *) ...... informuje navíc o typu odkazované proměnné

pointerová aritmetika (platná pro typové ukazatele)
  - k pointeru lze přičíst/odečíst celé číslo  =>  pointer se posune na další hodnotu (např. o 4 byty)
  - lze odečíst 2 pointery (stejných typů)     =>  výsledek určuje počet hodnot (ne bytů) mezi pointery
  - pointery lze porovnávat ( == != < <= > >= )

paměťové oblasti: KÓD      ... programové instrukce
                  DATA     ... globální proměnné
                  ZÁSOBNÍK ... lokální proměnné
                  HALDA    ... dynamické proměnné

Vstup a výstup řetězců

int scanf(const char *format, ...);
  preskočí úvodní bílé znaky a čte vše do následujícího bíleho znaku
  přečte jen 1 slovo
  neumí načíst prázdný řetězec
  lze omezit počet načítaných znaků
int printf(const char *format, ...);
  formátovací direktiva pro řetězec je %s
    
char *gets(char *s);
  jako Pascalská ReadLN
  čte celý řádek (až do '\n'), řetězec uloží do s bez znaku '\n' 
  zakončí '\0', prázdný řádek --> s[0]=='\0'
  NEBEZPECNA! Nekontroluje počet načtených znaků!
int puts(char *s);
  jako Pascalská WriteLN

char *fgets(char *s, int max, FILE *f);
  do pole ukládá i '\n'
  ze vstupního souboru čte omezený počet znaků
int fputs(char *s, FILE *f);
  jako pascalská Write

Řetězcové funkce

- typ  size_t  je 2B celé číslo bez znaménka
- rozsahy řetězců si musí hlídat programátor !!!
- funkční prototypy jsou v hlavičkovém souboru string.h
Základní řetězcové operace
size_t strlen(const char *s);
  vrací délku řetězce s
char *strcpy(char *cil, const char *zdroj);
  zkopíruje řetězec zdroj do řetězce cil
  vrací ukazatel na začátek řetězce cil
char *strncpy(char *cil, const char *zdroj, size_t pocet);
  zkopíruje pocet znaků řetězce zdroj do řetězce cil
  vrací ukazatel na začátek řetězce cil
char *strcat(char *cil, const char *zdroj);
  přidá řetězec zdroj na konec řetězce cil
  vrací ukazatel na začátek řetězce cil
char *strncat(char *cil, const char *zdroj, size_t pocet);
  přidá pocet znaků řetězce zdroj na konec řetězce cil
  vrací ukazatel na začátek řetězce cil
Porovnávání řetězců
int strcmp(const char *s1, const char *s2);
  porovnává řetězce s1,s2
  vrací  0  při s1=s2
         kladné číslo  při s1>s2 (tj. s1 je v abecedě za s2)
         záporné číslo při s1<s2
int strncmp(const char *s1, const char *s2, size_t pocet);
  porovnává pouze pocet znaků z obou řetězců
int strcasecmp(const char *s1, const char *s2);
  porovnává řetězce bez ohledu na velikost znaků ('a'=='A')
int strncasecmp(const char *s1, const char *s2, size_t pocet);
  porovnává pocet znaků řetězců bez ohledu na velikost znaků
int strcoll(const char *s1, const char *s2);
  porovnává řetězce s přihlédnutím na nastavení národního prostředí
Prohledávání řetězce
char *strchr(const char *s, int c);
char *index(const char *s, int c);
  hledá zleva znak c v řetězci s
  vrací ukazatel na nalezený znak nebo NULL při neúspěchu
char *strrchr(const char *s, int c);
char *rindex(const char *s, int c);
  hledá zprava znak c v řetězci s
  vrací ukazatel na nalezený znak nebo NULL při neúspěchu
char *strstr(const char *kde, const char *co);
  hledá podřetězec co v řetězci kde
  vrací ukazatel na začátek nalezeného podřetězce nebo NULL při neúspěchu
size_t strspn(const char *s, const char *mnozina);
  vrací počet prvních znaků řetězce s, které jsou obsaženy v řetězci mnozina
        (tj. pozici prvního nevyhovujícího znaku)
size_t strcspn(const char *s, const char *mnozina);
  vrací počet prvních znaků řetězce s, které nejsou obsaženy v řetězci mnozina
        (tj. pozici prvního vyhovujícího znaku)
char *strpbrk(const char *s, const char *mnozina);
  vrací ukazatel na první vyhovující znak v řetězci s, nebo NULL při neúspěchu
Funkce, které ignorují konec řetězce (znak '\0')
void *memset(void *s, int c, size_t pocet);
  vyplní pocet bytů paměti znakem c
  vrací ukazatel na oblast paměti
void *memcpy(void *cil, const void *zdroj, size_t pocet);
  zkopíruje pocet bytů z paměťové oblasti zdroj do cil
  vrací ukazatel na začátek oblasti cil
void *memccpy(void *cil, const void *zdroj, int c, size_t pocet);
  kopíruje pocet bytů z paměťové oblasti zdroj do cil,
  při nalezení znaku c kopírování zastaví
  vrací ukazatel na 1.byte za znakem c nebo NULL, není-li znak c nalezen
void *memmove(void *cil, const void *zdroj, size_t pocet);
  zkopíruje pocet bytů z paměťové oblasti zdroj do cil
  vrací ukazatel na začátek oblasti cil
  paměťové oblasti zdroj a cil se mohou překrývat !
int memcmp(const void *s1, const void *s2, size_t pocet);
  porovnává pocet bytů paměťových oblastí s1, s2
void *memchr(const void *s, int c, size_t pocet);
  hledá zleva znak c v paměti
  vrací ukazatel na nalezený znak nebo NULL při neúspěchu
void *memrchr(const void *s, int c, size_t pocet);
  hledá zleva znak c v paměti zprava
  vrací ukazatel na nalezený znak nebo NULL při neúspěchu
Převodní funkce řetězec-číslo
int atoi(const char *s);
long atol(const char *s);
double atof(const char *s);
  převede řetězec na číslo,
  neprovádí detekci chyb,
  hlavičky funkcí jsou v stdlib.h
int sscanf(char *s, const char *format, ... );
  formátované čtení z řetězce
int sprintf(char *s, const char *format, ... );
  formátovaný zápis do řetězce

Knihovny funkcí

Statická knihovna
Sestavovací program najde v knihovně programový kód a umístí jej do programu.
Program je pak větší, ale není závislý na knihovně.
překlad zdrojových souborů:                     gcc -c f1.c f2.c f3.c f4.c ...
zařazení přeložených souborů do knihovny moje:  ar r libmoje.a f1.o f2.o f3.o f4.o ... 
výpis souborů obsažených v knihovně:            ar t libmoje.a
výpis funkcí obsažených v knihovně:             nm libmoje.a
sestavení programu:                             gcc -o program program.c -L . -lmoje
Sdílená knihovna
Sestavovací program umístí do programu pouze odkaz na sdílenou knihovnu.
Při spuštění programu pak zavaděč sdílených knihoven nahraje knihovnu do paměti a tento kód je sdílen i ostatními procesy.
Při aktualizaci knihovny se zaktualizují i všechny programy na ní závislé bez nutnosti nového překladu.
překlad zdrojových souborů:                     gcc -c -fpic f1.c f2.c f3.c f4.c ...
zařazení přeložených souborů do knihovny moje:  gcc -shared -fpic -o libmoje.so f1.o f2.o f3.o f4.o ... 
výpis funkcí obsažených v knihovně:             nm libmoje.a
sestavení programu:                             gcc -o program program.c -L . -lmoje
výpis závislostí programu na knihovnách:        ldd program
poznámky

Režimy otevření souboru

        Otevřít pro:       Existuje-li soubor  Neexistuje-li soubor  Data lze číst  Data lze zapisovat
        ------------       ------------------  --------------------  -------------  ------------------
 r  ... čtení              o.k.                CHYBA                 odkudkoliv     X 
 w  ... zápis              bude přepsán        bude vytvořen         X              kamkoliv
 a  ... rozšíření          o.k.                bude vytvořen         X              na konec

 r+ ... čtení a zápis      o.k.                CHYBA                 odkudkoliv     kamkoliv
 w+ ... čtení a zápis      bude přepsán        bude vytvořen         odkudkoliv     kamkoliv
 a+ ... čtení a rozšíření  o.k.                bude vytvořen         odkudkoliv     na konec

 rb, wb, ab, rb+, wb+, ab+ ... totéž pro binární soubory
 

Funkce pro práci se soubory

FILE *fopen(const char *jmeno, const char *rezim);
  otevře soubor v zadaném režimu
  vrací ukazatel na strukturu FILE, při chybě NULL
int fclose(FILE *f);
  zapíše výstupní buffer a uzavře soubor
  vrací 0 při úspěchu, EOF (= -1) při chybě
int fflush(FILE *f);
  zapíše výstupní buffer
  vrací 0 při úspěchu, EOF (-1) při chybě
FILE *tmpfile(void);
  otevře dočasný soubor v režimu wb+, jedinečné jméno si funkce vygeneruje
  soubor bude automaticky smazán po uzavření
  vrací ukazatel na strukturu FILE, při chybě NULL
  
int fscanf(FILE *f, const char *format, ... );
  načte z textového souboru data, provede konverzi podle formátovacího řetězce a uloží do proměnných
  vrací počet úspěšně načtených položek
int fprintf(FILE *f, const char *format, ... );
  podle formátovacího řetězce zkonvertuje argumenty na text, který zapíše do souboru
  vrací počet zapsaných znaků

char *fgets(char *s, int max, FILE *f);
  přečte ze vstupního souboru řádek, počet znaků je ale omezen parametrem
  do řetězce ukládá i '\n'
  vrací ukazatel na začátek řetězce, při chybě NULL
int fputs(char *s, FILE *f);
  zapíše řetězec do souboru
  vrací nezáporné číslo, při chybě EOF (-1)

int fgetc(FILE *f);
  přečte znak ze souboru
  vrací přečtený znak (zkonvertovaný na typ int), při chybě EOF (-1)
int fputc(int c, FILE *f);
  zapíše znak do souboru
  vrací zapsaný znak, při chybě EOF (-1)
int ungetc(int c, FILE *f);
  vrátí znak do vstupního bufferu
  vrací tento znak, při chybě EOF (-1)

int fread(char *buffer, int velikost, int pocet, FILE *f);
  přečte ze souboru počet položek zadané velikosti a uloží je do bytového pole buffer
  vrací počet úspěšně přečtených položek (ne počet bytů)
int fwrite(char *buffer, int velikost, int pocet, FILE *f);
  zapíše do souboru počet položek zadané velikosti
  vrací počet úspěšně zapsaných položek
int fseek(FILE *f, long posun, int odkud);
  přesune aktuální pozici v souboru
  parametr odkud může mít hodnotu SEEK_SET ... od začátku souboru
                                  SEEK_CUR ... od aktuální pozice
                                  SEEK_END ... od konce souboru
  vrací 0 při úspěchu, -1 při chybě
long ftell(FILE *f);
  vrací aktuální pozici v souboru, -1 při chybě

int rename(const char *puvodni_jmeno, const char *nove_jmeno);
  přejmenuje soubor, soubor by měl být uzavřen
  vrací 0 při úspěchu, -1 při chybě
int remove(const char *jmeno_souboru);
  vymaže soubor, soubor by měl být uzavřen
  vrací 0 při úspěchu, -1 při chybě

void perror(const char *s);
  vypíše na standardní chybový výstup řetězec v argumentu funkce následovaný chybovým hlášením,
  které popisuje poslední chybu vzniklou při volání knihovní nebo systémové funkce
extern int errno;
  proměnná indikující chybový stav, je definována v errno.h
char *strerror(int cislo_chyby);
  vrací řetězec popisující chybu daného čísla, funkční prototyp je v string.h