| ← knihovna math.h | C/C++ | stdarg.h, limits.h, signal.h → |
Standardní knihovna <stdlib.h> deklaruje spoustu užitečných funkcí. Mnohé z nich jsme již v předchozím výkladu použili. Je dobré se tyto funkce naučit používat. Například funkce qsort() slouží pro třídění datových polí. Jistě by jste dokázali napsat vlastní funkci na třídění. Třeba v programu dynamic1.c jsme data třídili vlastním algoritmem. Použití funkce qsort() by však bylo rychlejší. A navíc pomocí ní lze třídit všelijaké struktury (to se naučíte za chvilku).
Toto není výčet všech funkcí z knihovny <stdlib.h>. Prostudujte si vaši dokumentaci k jazyku C, chcete-li znát všechny funkce.
Kromě těchto funkcí jsou v souboru <stdlib.h> definována i některá užitečná makra. Jejich používání zvyšuje přenositelnost programu.
Vezměme to hezky popořádku. Funkci atoi() jsme již používali. Je to tak jednoduché, že snad není třeba nic dodávat. Funkce rand() a srand() také již znáte. Funkce calloc(), malloc(), free() a realloc() jsou popisovány v kapitole o dynamické alokaci paměti.
Použití funkce exit() jste již viděli, ale ještě si k ní něco povíme. Víte, že funkce exit() ukončí program kdekoliv je volána. Volání funkce exit(x) je stejné, jak by jste volali return x ve funkci main(). Hodnota x je vrácena do prostředí, ve kterém je program spuštěn, popřípadě procesu, který tento program spustil (ukážeme si v příkladě na funkci system()). Tuto hodnotu lze testovat třeba v dávkových souborech (.BAT) v DOSu nebo v unixových skriptech shellu. Není však účelem tohoto článku zde popisovat dávkové soubory. Stačí jen když budete vědět, že nulová návratová hodnota se považuje za bezchybné ukončení programu a díky tomu skripty (dávkové soubory) zjišťují, zda program udělal to co měl, či ne. Návratovou hodnotou různých čísel také můžete informovat o různých příčinách konce programu. Není tedy vůbec nutné brát nenulovou hodnotu jako chybu.
V systémech Unix je možné zjistit návratový kód programu v systémové proměnné ?. Například příkazem echo $?.
Některé návratové hodnoty mají v různých systémech různý specifický význam. Proto, pokud nemáte zvláštní důvod činit jinak, ukončujte funkce (program) s návratovou hodnotou 0 (EXIT_SUCCESS) v případě úspěchu a 1 (EXIT_FAILURE) v případě neúspěchu.
Funkce exit() navíc před ukončením programu (stejně jako return ve funkci main()) spustí funkce registrované pomocí atexit(). Kde a jak jsou registrované, o to se nemusíte starat. Důležité je vědět, že jsou tyto registrované funkce volány vždy při skončení programu, nedojde-li k jeho ukončení funkcí abort() nebo jinému nestandardnímu konci programu. Využít se toho dá mnoha způsoby. Například můžete vytvořit při spuštění programu nějaký dočasný soubor, který takto registrovanou funkcí smažete. Pokud se program spustí a tento soubor existuje, znamená to, že je program spuštěný vícekrát, nebo že byl nestandardně ukončen.
Funkce system() udělá zhruba to, co by udělal operační systém po zadání příkazu, který je argumentem funkce system. Použití této funkce si ukážeme v příkladu, nic těžkého na tom není.
Funkci getenv() budu vysvětlovat v části věnované programování v Linuxu. Její použití je totiž silně závislé na prostředí, ve kterém je program spouštěn. Využívá se například v souvislosti s dávkovými soubory.
Co se týče funkcí div() a ldiv(), ukážu jen definici struktury div_t. Pokud umíte se strukturami pracovat, nepotřebujete znát více. Hlavně si musíte dát pozor na dělení nulou!. To by váš program zabilo!.
1: typedef struct { 2: int quot; /* Quotient. */ 3: int rem; /* Remainder. */ 4: } div_t;
Pochopení funkce qsort() bude zřejmě nejtěžší. Funkci qsort() se ještě budeme věnovat v kapitole o třídění. Tato funkce třídí prvky na základě návratové hodnoty funkce, na kterou ukazuje compar (viz ukazatele na funkce). Jelikož je tento ukazatel deklarován jako int (*compar)(const void *, const void *);, je třeba funkci která provádí porovnávání prvků z tříděného pole přetypovat. Řekněme, že naše třídící funkce se bude jmenovat tridici_funkce. Její návratová hodnota musí být typ int. Argumenty budou nějaké datové typy nebo struktury, ale při volání funkcí qsort() musí být tato funkce přetypována na funkci s argumenty typu void *. To se provede následujícím způsobem.
qsort(tridene_pole, POCET_PRVKU_POLE, sizeof(prvek_pole), (int (*) (const void *, const void *)) tridici_funkce);
V prvním příkladě vytvoříme pole struktury cisla obsahující typ div_t a dvě čísla, jejíž podíl a zbytek po dělení bude v právě v proměnné typu div_t. Toto pole pak setřídíme pomocí qsort(). K porovnávání prvků si vytvoříme funkci s názvem srovnej_cislo().
1: /*------------------------------------------------*/ 2: /* qsort1.c */ 4: #include <stdio.h> 5: #include <stdlib.h> 7: #define DELKAPOLE 20 9: typedef struct { 10: int x, y; 11: div_t podil; 12: } cisla; 14: int srovnej_cislo(cisla * a, cisla * b) 15: { 16: /* nejdrive tridime podle zbytku */ 17: if (a->podil.rem > b->podil.rem) 18: return 1; 19: else if (a->podil.rem < b->podil.rem) 20: return -1; 21: /* kdyz jsou si zbytky rovny, tridime podle 22: * podilu */ 24: else if (a->podil.quot > b->podil.quot) 25: return 1; 26: else if (a->podil.quot < b->podil.quot) 27: return -1; 28: else 29: return 0; /* jsou si rovny */ 30: } 32: int main(void) 33: { 34: int i; 35: cisla pole[DELKAPOLE]; 36: for (i = 0; i < DELKAPOLE; i++) { 37: pole[i].x = i; 38: pole[i].y = 5; 39: pole[i].podil = div(pole[i].x, pole[i].y); 40: } 42: qsort(pole, sizeof(pole) / sizeof(pole[0]), sizeof(pole[0]), 43: (int (*)(const void *, const void *)) srovnej_cislo); 45: for (i = 0; i < DELKAPOLE; i++) 46: printf("%2i/%2i = %2i (%2i)\n", pole[i].x, pole[i].y, 47: pole[i].podil.quot, pole[i].podil.rem); 49: return 0; 50: } 52: /*------------------------------------------------*/
Výstup z programu:
0/ 5 = 0 ( 0) 5/ 5 = 1 ( 0) 10/ 5 = 2 ( 0) 15/ 5 = 3 ( 0) 1/ 5 = 0 ( 1) 6/ 5 = 1 ( 1) 11/ 5 = 2 ( 1) 16/ 5 = 3 ( 1) 2/ 5 = 0 ( 2) 7/ 5 = 1 ( 2) 12/ 5 = 2 ( 2) 17/ 5 = 3 ( 2) 3/ 5 = 0 ( 3) 8/ 5 = 1 ( 3) 13/ 5 = 2 ( 3) 18/ 5 = 3 ( 3) 4/ 5 = 0 ( 4) 9/ 5 = 1 ( 4) 14/ 5 = 2 ( 4) 19/ 5 = 3 ( 4)
V následujícím příkladě si ukážeme volání funkcí exit(), abort() a použití funkce atexit(). Program bude vracet hodnotu, kterou uživatel zadá z příkazové řádky.
1: /*------------------------------------------------*/ 2: /* konec.c */ 4: #include <stdio.h> 5: #include <stdlib.h> 7: void koncim1(void) 8: { 9: printf("Elektrina odpojena!\n"); 10: } 12: void koncim2(void) 13: { 14: printf("Systemy vypnuty!\n"); 15: } 17: int main(int argc, char *argv[]) 18: { 20: atexit(koncim1); 21: atexit(koncim2); /* nejdrive probehne funkce koncim2 a pak koncim1, 22: tedy v obracenem poradi nez byli registrovany */ 24: if (argc != 2) { 25: printf("Na prikazove radce nebyla zadana navratova hodnota!\n"); 26: abort(); 27: } 28: exit(atoi(argv[1])); 30: return 0; /* tato cast programu za exit nikdy neprobehne */ 31: } 33: /*------------------------------------------------*/
Podívejte se, jak se chová program v systému Linux.
konec // prikaz v Linuxu - spusteni programu Na prikazove radce nebyla zadana navratova hodnota! Neúspěšně ukončen (SIGABRT) // hlaseni operacniho systemu echo $? // prikaz v Linuxu - vypis navratove hodnoty programu 134
V DOSu se místo „Neúspěšně ukončen (SIGABRT)“ ukáže „Abnormal program termination“. Dalším možnost spuštění (v Linuxu):
konec 5 Systemy vypnuty! Elektrina odpojena! echo $? 5
Tento program (se jménem konec) využijeme i v následujícím příkladě (se jménem system1). Ukážeme v něm použití funkce system(), která bude program konec volat. Program konec se bude hledat ve stejném adresáři, ve kterém bude program system1 spuštěn.
Jména programů jsou v systému DOS a Windows samozřejmě zakončena příponou .exe, ale to na příkladě nic nemění.
Problém je však v návratové hodnotě funkce system(). V případě neúspěšného spuštění příkazového interpretu (např. bash nebo command.com) vrátí 127, v případě neúspěchu spuštění příkazu (např. příkaz neexistuje) vrátí -1. V případě, že příkaz (zadaný funkci system() jako textový řetězec) proběhne, vrátí funkce system() jeho návratový kód. Ovšem v Linuxu je tento návratový kód pozměněn a proto se musí na něj aplikovat makro WEXITSTATUS definované v souboru <sys/wait.h>. Proto je v příkladu použit podmíněný překlad a pro neunixové systémy je makro WEXITSTATUS definováno tak, že nic nedělá.
1: /*------------------------------------------------*/ 2: /* system1.c */ 4: #include <stdio.h> 5: #include <stdlib.h> 7: #ifdef unix 8: #include <sys/wait.h> 9: #endif 11: #ifndef unix 12: #define WEXITSTATUS(X) X 13: #endif 15: int main(void) 16: { 17: int navrat; 19: printf("Spoustim program> konec\n"); 20: navrat = system("konec"); 21: printf("Program skoncil s navratovou hodnotou %i\n\n", 22: WEXITSTATUS(navrat)); 24: printf("Spoustim program> konec 5\n"); 25: navrat = system("konec 5"); 26: printf("Program skoncil (%i)\n\n", WEXITSTATUS(navrat)); 28: return 0; 29: } 31: /*------------------------------------------------*/
Výstup z programu:
Spoustim program> konec Na prikazove radce nebyla zadana navratova hodnota! Neúspěšně ukončen (SIGABRT) Program skoncil s navratovou hodnotou 134 Spoustim program> konec 5 Systemy vypnuty! Elektrina odpojena! Program skoncil (5)
| ← knihovna math.h | C/C++ | stdarg.h, limits.h, signal.h → |
