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
- soubor s knihovnou se musí jmenovat libjméno_knihovny.a resp. libjméno_knihovny.so
- ld je sestavovací program (linker), spouští ho gcc
- aby linker našel naše knihovny, musíme mu napsat, kde jsou: gcc -L adresář
- pokud najde linker statickou i sdílenou knihovnu, použije sdílenou
- použití statické knihovny můžeme vnutit: gcc -static
- při sestavování programu se statickou knihovnou pište knihovnu na konec příkazové řádky
- ld-linux.so je zavaděč sdílených knihoven
- aby zavaděč našel naše knihovny, vytvoříme systémovou proměnnou: export LD_LIBRARY_PATH=adresář
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