Knihovna stdlib.h
Standardní knihovna <stdlib.h> deklaruje spoustu užitečných
funkcí. Mnohé z nich jsem již v předchozím výkladu použil. Je
dobré se tyto funkce naučit používat. Například funkce
qsort()
slouží pro třídění datových polí.
Jistě byste dokázali napsat vlastní funkci na třídění.
V programu dynamic1.c
jsem data třídil vlastním algoritmem. Použití funkce
qsort()
by však bylo rychlejší – jak z pohledu výkonu programu,
tak z pohledu doby psaní programátorem. A navíc, od programátorem nově vymýšlené
fukce je už tato odladěná, bezchybná…
Výčet funkcí
- Převede řetězec nptr na číslo vzhledem k číselné soustavě base. Adresu prvního znaku, který nepatří do čísla, uloží do endptr, pokud endptr není nastaveno na NULL.
- Převede řetězec nptr na číslo typu int. Tato funkce
nedetekuje žádné chyby. Její chování je totožné s voláním
strtol(nptr, (char **)NULL, 10);
- Převede řetězec nptr na číslo typu double. Tato funkce nedetekuje žádné chyby.
- Převede řetězec nptr na číslo typu long int. Tato funkce nedetekuje žádné chyby.
- Inicializuje generátor pseudonáhodných čísel.
- Vrací pseudonáhodná čísla mezi 0 a RAND_MAX
- Dynamicky alokuje paměť nmemb položek velikosti size. Do alokované paměti zapíše nuly. Vrací ukazatel na alokovanou paměť nebo NULL v případě chyby.
- Dynamicky alokuje paměť velikosti size. Paměť nevynuluje. Vrací ukazatel na alokovanou paměť nebo NULL v případě chyby.
- Uvolní dynamicky alokovanou paměť (pomocí
malloc()
,calloc()
neborealloc()
) na kterou ukazuje ptr. - Změní velikost dynamicky alokované paměti ptr na velikost size.
- Způsobí abnormální ukončení programu. Chování funkce je implementačně závislé.
- Funkce exit ukončí program kdekoliv je v programu volána. Program pak
vrací operačnímu systému hodnotu status. Všechny funkce registrované funkcemi
atexit()
aon_exit()
jsou vyvolány v opačném pořadí jejich registrace. - Přiřadí funkci do seznamu funkcí, které jsou volány při
normálním ukončení programu funkcí
exit()
nebo skončením funkcemain()
. Argument function je ukazatelem na funkci bez argumentů a bez návratové hodnoty. Funkce takto registrované jsou volány v opačném pořadí než jsou registrovány. Funkceatexit()
vrací 0 v případě úspěchu, -1 v případě chyby (např. nedostatek paměti pro registraci funkce). -
Volá systémový příkaz určený řetězcem string. Například
tak můžete zavolat příkaz DOSu
"COPY c:\soubor1.txt c:\soubor2.txt". V Linuxu se
nedoporučuje používat funkci
system()
v programech se superuživatelskými právy. Toho by šlo totiž snadno zneužít zlými lidmi. Nepodaří-li se spustit shell (příkazový řádek), vrací funkcesystem()
hodnotu 127, v případě jiné chyby -1. V ostatních případech vrací návratový kód příslušného příkazu. - Vyhledá hodnotu prostředí systému určenou řetězcem name. Vrátí ukazatel na tuto hodnotu, nebo NULL v případě neúspěšného hledání. Hodnoty prostředí jsou tvořeny řetězci ve formě název=hodnota. Tuto funkci budou asi využívat hlavně příznivci Linuxu, kde se jedná například o hodnoty USER, LOGNAME, HOME, PATH, SHELL, TERM apod.
- Vrací absolutní hodnotu argumentu typu int.
- Vrací absolutní hodnotu argumentu typu long int.
- Počítá podíl numer/denom. Podíl a zbytek po
dělení vrací ve struktuře
div_t
. Tato struktura obsahuje složku quot, která obsahuje podíl a složku rem, která obsahuje zbytek. Obě složky jsou celočíselné. Strukturadiv_t
je také definována v <stdlib.h> - Jako funkce
div()
, jenom je všechno v dlouhých celých číslech. - Funkce
qsort()
třídí algoritmem quicksort souvislé pole libovolného typu (!), na které ukazuje base o nmemb položkách velikosti size. K třídění je použita funkce na kterou ukazuje compar. Funkcicompar()
budou při volání předávány jako argumenty prvky z pole base. Funkcecompar()
musí vracet zápornou hodnotu, pokud je první argument menší než druhý, nulovou hodnotu pokud se rovnají a kladnou hodnotu pokud je první argument větší než druhý. -
Vyhledá prvek key v poli base dlohém nmemb
položek. Položka je velikosti size a k nalezení prvku se použije
porovnávací funkce
compar()
, která vrací 0 v případě rovnosti prvků. Pole base musí být vzestupně setříděné!
Toto není úplný výčet funkcí z knihovny <stdlib.h>. Prostudujte si vaši dokumentaci k jazyku C, chcete-li znát všechny funkce.
Užitečná makra
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.
- EXIT_FAILURE
- Hodnota argumentu funkce
exit()
pro oznámení neúspěšného ukončení programu. (nenulová hodnota) - EXIT_SUCCESS
- Hodnota argumentu funkce
exit()
pro úspěšné ukončení programu. (nulová hodnota) - NULL
- Nulový ukazatel.
- RAND_MAX
- Největší hodnota generovaná funkcí
rand()
.
Poznámky k funkcím
Vezměme to hezky popořádku. Funkci atoi()
jsem již ukazoval. 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ě vám k ní něco
povím. Víte, že funkce exit()
ukončí program kdekoliv je volána.
Volání funkce exit(x)
je stejné,
jak byste 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áži 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 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, je to ale dobrý zvyk.
V systémech Unix je možné zjistit návratový kód naposledy spuštěného
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 ukáži 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 pracovat se strukturami,
nepotřebujete znát více. Hlavně si musíte dát
pozor na dělení nulou!. To by váš program zabilo!
Pochopení funkce qsort()
bude
zřejmě nejtěžší. Funkci qsort()
se ještě budu 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 typu 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:
Příklad použití
V prvním příkladě vytvořím pole struktury cisla
obsahující
typ div_t
a dvě čísla, jejichž podíl a zbytek po dělení bude v
právě v proměnné typu div_t
.
Toto pole pak setřídím pomocí qsort()
.
K porovnávání prvků si vytvořím funkci s názvem
srovnej_cislo()
.
- /*------------------------------------------------*/
- /* c22/qsort1.c */
- #include <stdio.h>
- #include <stdlib.h>
- #define DELKAPOLE 20
- div_t podil;
- } cisla;
- {
- /* nejdrive tridim podle zbytku */
- /* kdyz jsou si zbytky rovny, tridim podle
- * podilu */
- }
- {
- size_t i;
- cisla pole[DELKAPOLE];
- pole[i].x = i;
- pole[i].y = 5;
- }
- pole[i].podil.quot, pole[i].podil.rem);
- }
- }
- /*------------------------------------------------*/
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ě ukáži volání funkcí
exit()
, abort()
a použití funkce
atexit()
. Program bude vracet
hodnotu, kterou uživatel zadá z příkazové řádky.
- /*------------------------------------------------*/
- /* c22/konec.c */
- #include <stdio.h>
- #include <stdlib.h>
- {
- }
- {
- }
- {
- /* nejdrive probehne funkce koncim2 a pak koncim1,
- tedy v obracenem poradi nez byli registrovany */
- }
- /* tato cast programu za exit nikdy neprobehne */
- }
- /*------------------------------------------------*/
Podívejte se, jak se chová program v systému Linux.
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):
Systemy vypnuty!
Elektrina odpojena!
echo $?
5
Tento program (se jménem konec) využiji i v následujícím
příkladě (se jménem system1). Ukáži 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 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á.
- /*------------------------------------------------*/
- /* c22/system1.c */
- #include <stdio.h>
- #include <stdlib.h>
- #if defined unix || defined __unix__
- #include <sys/wait.h>
- #else
- #define WEXITSTATUS(X) X
- #endif
- #ifdef _MSC_VER
- #define KONEC "..\\Debug\\konec.exe"
- #else
- #define KONEC "./konec"
- #endif
- {
- int navrat;
- /* printf("Bezim zde: %s\n", getcwd()); */
- WEXITSTATUS(navrat));
- }
- /*------------------------------------------------*/
Visual Studio ukládá spustitelné soubory do podsložky Debug
. Je ale možné, že budete
muset trochu experimentovat, než najdete správnou cestu k programu "konec.exe". Třeba vám k tomu
pomůže funkce getcwd()
.
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)