| ← knihovna stdlib.h | C/C++ | knihovna errno.h → |
Tato knihovna slouží k práci funkcí s proměnlivým počtem argumentů. Popis maker, které obsahuje a jejich použití je probráno v kapitole o proměnlivém počtu argumentů.
Knihovna <limits.h> obsahuje makra určující rozsah hodnot celočíselných datových typů. Tyto makra lze použít i v preprocesoru pro podmíněný překlad, jinak se nejčastěji používají v podmínkách programu. Hodnoty, které vidíte v tabulce jsou jen orientační a v různých implementacích překladačů se liší (jinak by snad ani neměli význam). Například INT_MAX je pro 16bitové překladače 32767 a pro 32bitové 2147483647. Makra začínající písmenem U jsou pro čísla unsigned (čísla bez znaménka, např UINT_MAX). Jde vždy o hodnotu MAX, protože MIN je u čísel bez znaménka pochopitelně 0 (např. UINT_MIN není definováno).
| Makro | Hodnota | Význam |
|---|---|---|
| CHAR_BIT | 8 | Počet bitů v datovém typu char. |
| SCHAR_MIN | -128 | Minimální hodnota typu signed char (typ char se znaménkem). |
| SCHAR_MAX | 127 | Maximální hodnota typu signed char |
| UCHAR_MAX | 255 | Maximální hodnota typu unsigned char |
| SHRT_MIN | -32768 | Minimální hodnota typu signed short int (krátké celé číslo se znaménkem) |
| SHRT_MAX | 32767 | Maximální hodnota typu signed short int |
| USHRT_MAX | 65535 | Maximální hodnota typu unsigned short int |
| INT_MIN | (-INT_MAX -1) | Minimální hodnota typu signed int |
| INT_MAX | 2147483647 | Maximální hodnota typu signed int |
| UINT_MAX | 4294967295U | Maximální hodnota typu unsigned int |
| LONG_MAX | 2147483647L | |
| LONG_MIN | (-LONG_MAX - 1L) | |
| ULONG_MAX | 4294967295UL |
Signály slouží k oznámení nějaké události. Například při dělení nulou je poslán signál SIGFPE. Definice signálů najdete v knihovně <signal.h>. Ve chvíli, kdy je vašemu programu poslán signál, provádění programu se pozastaví a signál je zpracován příslušnou funkcí signálu přiřazenou. Některé signály lze ignorovat a některé naopak nelze ani zpracovat ani ignorovat (např. signál SIGKILL). Pokud signálu žádnou funkci nepřiřadíte, bude provedena standardní obsluha signálu. Například při dělení nulou to bude mít za následek ukončení programu. Signály nemusí být vyvolány jen událostmi programu, ale mohou být posílány programu operačním systémem nebo jinými programy (v Linuxu můžete programu poslat signál pomocí příkazu kill()).
| Signál | Význam |
|---|---|
| SIGINT | Přerušení provádění programu |
| SIGABRT | Zastavení programu |
| SIGILL | Neplatná instrukce (např. instrukce procesoru Pentium na stroji i386 |
| SIGFPE | Aritmetická chyba (dělení nulou, výsledek mimo rozsah) |
| SIGSEGV | Chybný přístup do paměti.(Program čte z míst v paměti, ze kterých by neměl). |
| SIGTERM | Ukončení (linuxový příkaz kill) |
Protože se budu zabývat v budoucnu programováním v Linuxu, uvedu zde ještě tabulku některých signálů podle normy POSIX.
| Signál | Význam |
|---|---|
| SIGHUP | problém |
| SIGQUIT | opuštění terminálu |
| SIGKILL | zabití procesu (nelze odchytit) |
| SIGUSR1 | signál definovaný uživatelem |
| SIGUSR2 | další signál definovaný uživatelem |
| SIGPIPE | nefungující roura (např. když z ní nikdo nečte) |
| SIGALARM | budík |
| SIGCHLD | dceřiný proces se změnil (např. se zastavil nebo skončil) |
| SIGCONT | pokračovat (byl-li proces pozastaven) |
| SIGSTOP | zastavit provádění (nelze odchytit) |
| SIGTSTP | zastavit provádění (signál z klávesnice) |
| SIGTTIN | čtení procesu spuštěného na pozadí (ze standardního vstupu) |
| SIGTTOU | proces na pozadí se pokouší zapisovat |
Pokud některý ze signálů SIGHUP – SIGALARM v programu neodchytíte, dojde k ukončení běhu programu. K odchytávání signálů, respektive k přiřazení signálům funkce slouží funkce signal(). Podívejte se na její deklaraci:
void (*signal(int signum, void (*handler)(int)))(int);
Funkce signal má jako první argument (signum) signál, kterému je přiřazena funkce handler. Funkce handler() nemá žádnou návratovou hodnotu a musí mít jeden argument typu int, kterým je předávána hodnota signálu, který způsobil volání funkce. Jedna funkce totiž může být přiřazena libovolnému množství signálů. Funkce signal vrací ukazatel na funkci, která byla signálu dosud přiřazena, SIG_ERR v případě chyby, nebo jednu z následujících hodnot:
| Makro | Význam |
|---|---|
| SIG_IGN | ignoruj signál |
| SIG_DFL | implicitní chování funkce |
Tyto hodnoty lze použít ve funkci signal jako druhý argument (tedy místo funkce obsluhující signál). Možná to na první pohled vypadá trošku složitě, ale v příkladu uvidíte, že to nemůže být snazší.
V příkladu budeme odchytávat signál SIGFPE
po dělení nulou.
Nejdříve vytvoříme globální proměnou chyceni_signalu, ve
které budeme počítat, kolikrát byl signál vyvolán. Při každém
vyvolání signálu je nastaveno původní přiřazení signálu, proto ve
funkci zpracuj_signal() funkci znovu
signálu SIGFPE přiřadíme.1)
Po skončení funkce obsluhující signál se program vrátí do místa ve
kterém byl přerušen. V našem případě to znamená, že se pokusí znovu
dělit nulou.
Podrobněji se budeme věnovat odchytávání signálů až v části věnované programování v systému Linux.
1: /*------------------------------------------------*/ 2: /* signal1.c */ 4: #include <stdio.h> 5: #include <signal.h> 7: static int chyceni_signalu; 9: int a = 5, b = 0; 11: void zpracuj_signal(int cislo_signalu) 12: { 13: chyceni_signalu++; 14: switch (cislo_signalu) { 15: case SIGFPE: 16: printf 17: ("Delit nulou se nevyplaci! a= %i, b= %i, chyceni_signalu= %i\n", 18: a, b, chyceni_signalu); 19: break; 20: default: 21: printf("Neocekavany signal!\n"); 22: break; 23: } 24: if (chyceni_signalu >= 3) { 25: printf("Uz toho mam dost!\n"); 26: (void) signal(SIGFPE, SIG_DFL); 27: } else { 28: (void) signal(SIGFPE, zpracuj_signal); 29: } 30: } 32: int main(void) 33: { 35: void (*ukazatel_na_fci) (int); 37: ukazatel_na_fci = signal(SIGFPE, zpracuj_signal); 38: if (ukazatel_na_fci == SIG_DFL) { 39: printf 40: ("To je poprve, co nastavuji zpracovani signalu pro SIGFPE\n"); 41: } 43: printf("Zaciname: \n"); 44: printf("Deleni: %i/%i=%i\n", a, b, a / b); 45: printf("Tento text se jiz nevytiskne."); 47: return 0; 48: } 50: /*------------------------------------------------*/
Výstup z programu v prostředí Linux:
To je poprve, co nastavuji zpracovani signalu pro SIGFPE Zaciname: Delit nulou se nevyplaci! a= 5, b= 0, chyceni_signalu= 1 Delit nulou se nevyplaci! a= 5, b= 0, chyceni_signalu= 2 Delit nulou se nevyplaci! a= 5, b= 0, chyceni_signalu= 3 Uz toho mam dost! Výjimka matematického koprocesoru (SIGFPE)
Převzato z manuálových stránek k funkci signal(): Podle normy POSIX není definováno chování procesu poté, co ignoruje signál SIGFPE, SIGILL, nebo SIGSEGV, který nebyl generován funkcemi kill nebo raise. Celočíselné dělení nulou dává nedefinovaný výsledek a na některých architekturách generuje signál SIGFPE. Ignorování tohoto signálu může způsobit zacyklení procesu.
Jedna perlička k příkladu na závěr. Pokud by jste v programu uvedli výraz ve kterém by jste dělili nulou přímo a ne přes proměnné jako v předchozím příkladě, pak by to překladač odhalil a buď by program nepřeložil, nebo by za výraz dosadil nulu. V takovém případě by za běhu programu k dělení nulou nedošlo.
Funkce signal() volá funkci sigaction(). Funkce sigaction() je o něco složitější, protože má více možností jak ovlivnit chování signálů. My si jen ukážeme, jak tuto funkci použít namísto signal().
Funkce sigaction() odpovídá normě POSIX. To znamená, že máte zaručeno, že ji budete mít definovanou v Unixech, ale ve Windows ji pravděpodobně mít k dispozici nebudete. Ve windows si budete muset vystačit s funkcí signal() (viz víše).
Deklarace funkce vypadá takto:
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
Návratovou hodnotou je 0 v případě úspěchu a -1 v případě chyby (v programu, ač se to správně nedělá, budeme návratovou hodnotu ignorovat). První argument je signál, který chceme odchytávat, druhý argument je struktura, která nastavuje odchytávání signálu a do třetího argumentu se uloží staré nastavení pro daný signál signum.
Prohlédněte si definici struktury sigaction (ano, má stejné jméno jako funkce sigaction()).
struct sigaction {
void (*sa_handler)(int);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
}
První argument struktury sigaction je ukazatel na obslužnou funkci. Druhý argument ovlivňuje zachytávání signálů během volání obslužné funkce a pro nás je nezajímavý. Třetí argument, sa_flags, je množina příznaků, která ovlivňuje obsluhu signálu. Například SA_RESTART způsobí, že po zpracování signálu je znovu nastavena naše obsluha signálu a nikoliv implicitní akce. Poslední argument, sa_restorer, je zastaralý a již se nepoužívá.
Příklad sigaction.c dělá to samé, co příklad signal1.c
1: /*------------------------------------------------*/ 2: /* sigaction.c */ 4: #include <stdio.h> 5: #include <signal.h> 7: static int chyceni_signalu; 9: int a = 5, b = 0; 11: void zpracuj_signal(int cislo_signalu) 12: { 13: chyceni_signalu++; 14: struct sigaction sa = { zpracuj_signal, 0, SA_RESTART }; 15: switch (cislo_signalu) { 16: case SIGFPE: 17: printf 18: ("Delit nulou se nevyplaci! a= %i, b= %i, chyceni_signalu= %i\n", 19: a, b, chyceni_signalu); 20: break; 21: default: 22: printf("Neocekavany signal!\n"); 23: break; 24: } 25: if (chyceni_signalu >= 3) { 26: printf("Uz toho mam dost!\n"); 27: sa.sa_handler = SIG_DFL; 28: } 29: (void) sigaction(SIGFPE, &sa, NULL); 30: } 32: int main(void) 33: { 35: void (*ukazatel_na_fci) (int); 36: struct sigaction sb, sa = { zpracuj_signal, 0, SA_RESTART }; 38: (void) sigaction(SIGFPE, &sa, &sb); 39: if (sb.sa_handler == SIG_DFL) { 40: printf 41: ("To je poprve, co nastavuji zpracovani signalu pro SIGFPE\n"); 42: } 44: printf("Zaciname: \n"); 45: printf("Deleni: %i/%i=%i\n", a, b, a / b); 46: printf("Tento text se jiz nevytiskne."); 48: return 0; 49: } 51: /*------------------------------------------------*/
1)To platilo dříve. Dnes platí, že obslužná funkce zůstává stále zaregistrována. Kvůli tomuto historickému zmatení se doporučuje místo funkce signal() používat funkci sigaction().
| ← knihovna stdlib.h | C/C++ | knihovna errno.h → |
