Setjmp.h - Setjmp.h
![]() | tento článek potřebuje další citace pro ověření.Prosinec 2016) (Zjistěte, jak a kdy odstranit tuto zprávu šablony) ( |
C standardní knihovna |
---|
Obecná témata |
Různé záhlaví |
setjmp.h je záhlaví definované v C standardní knihovna poskytnout „nelokální skoky“: regulační tok která se odchyluje od obvyklých podprogram sekvence volání a zpět. Doplňkové funkce setjmp
a longjmp
poskytovat tuto funkci.
Typické použití setjmp
/longjmp
je implementace mechanismus výjimek který využívá schopnosti longjmp
obnovit stav programu nebo vlákna, a to i na více úrovních volání funkcí. Méně časté použití setjmp
je vytvořit syntaxi podobnou coutiny.
Členské funkce
int setjmp (jmp_buf env) | Nastaví místní jmp_buf vyrovnávací paměť a inicializuje ji pro skok. Tato rutina[1] uloží prostředí volajícího programu do vyrovnávací paměti prostředí určené parametrem env argument pro pozdější použití uživatelem longjmp . Pokud je návratka z přímého vyvolání, setjmp vrací 0. Pokud je návrat z volání na longjmp , setjmp vrací nenulovou hodnotu. |
void longjmp (jmp_buf env, int hodnota) | Obnoví kontext vyrovnávací paměti prostředí env který byl uložen vyvoláním setjmp rutina[1] ve stejném vyvolání programu. Vyvolávání longjmp z vnořeného obslužného programu signálu je nedefinováno. Hodnota určená hodnota je předán z longjmp na setjmp . Po longjmp je dokončeno, provádění programu pokračuje, jako by příslušné vyvolání setjmp právě se vrátil. Pokud hodnota přešel na longjmp je 0, setjmp bude se chovat, jako by to vrátilo 1; jinak se bude chovat, jako by se vrátil hodnota . |
setjmp
uloží aktuální prostředí (stav programu), v určitém okamžiku spuštění programu, do datové struktury specifické pro platformu (jmp_buf
), které lze použít v pozdějším okamžiku spuštění programu longjmp
obnovit stav programu do stavu uloženého pomocí setjmp
do jmp_buf
. Tento proces si lze představit jako „skok“ zpět do bodu provádění programu, kde setjmp
zachránil prostředí. (Zjevné) návratová hodnota z setjmp
označuje, zda ovládání dosáhlo tohoto bodu normálně (nula) nebo z hovoru na longjmp
(nenulové). To vede ke společnému idiom: -li( setjmp(X) ){/ * zpracovat longjmp (x) * /}
.
POSIX.1 neurčuje, zda setjmp
a longjmp
uložit a obnovit aktuální sadu blokovaných signály; pokud program využívá zpracování signálu, měl by používat POSIX sigsetjmp
/siglongjmp
.
Typy členů
jmp_buf | Typ pole, například struct __jmp_buf_tag [1] ,[2] vhodné pro uchovávání informací potřebných k obnovení volajícího prostředí. |
Odůvodnění C99 popisuje jmp_buf
jako typ pole pro zpětná kompatibilita; stávající kód odkazuje na jmp_buf
umístění úložiště podle názvu (bez &
address-of operator), což je možné pouze u typů polí.[3]
Upozornění a omezení
Když je provedeno „nelokální goto“ prostřednictvím setjmp
/longjmp
v C ++ normálníodvíjení stohu "nedojde. Nedojde tedy ani k žádným akcím čištění. Mohlo by to zahrnovat zavření." deskriptory souborů, proplachování Nárazníky nebo osvobození halda přidělená paměť.
Pokud funkce, ve které setjmp
byl nazýván návraty, již není možné bezpečně používat longjmp
s odpovídajícím jmp_buf
objekt. Je to proto, že rám zásobníku je neplatný, když se funkce vrátí. Povolání longjmp
obnovuje ukazatel zásobníku, který - protože se funkce vrátila - by ukazoval na neexistující a potenciálně přepsaný nebo poškozený rámec zásobníku.[4][5]
Podobně, C99 to nevyžaduje longjmp
zachovat aktuální rámec zásobníku. To znamená, že skok do funkce, která byla ukončena prostřednictvím volání longjmp
není definováno.[6] Většina implementací longjmp
ponechte rám stohu neporušený, povolte setjmp
a longjmp
slouží k přeskakování mezi dvěma nebo více funkcemi - funkce využívaná pro multitasking.
Ve srovnání s mechanismy v programovacích jazycích vyšší úrovně, jako je Krajta, Jáva, C ++, C#, a dokonce i pre-C jazyky jako Algol 60, technika používání setjmp
/longjmp
implementovat mechanismus výjimek je těžkopádné. Tyto jazyky poskytují výkonnější zpracování výjimek techniky, zatímco jazyky jako Systém, Pokec, a Haskell poskytnout ještě obecnější pokračování - manipulační konstrukce.
Příklad použití
Jednoduchý příklad
Následující příklad ukazuje základní myšlenku setjmp. Tam, hlavní()
hovory za prvé()
, což zase volá druhý()
. Pak, druhý()
skočí zpět do hlavní()
, přeskakování za prvé()
volání uživatele printf ()
.
#zahrnout <stdio.h>#zahrnout <setjmp.h>statický jmp_buf buf;prázdnota druhý() { printf("druhý n"); // tiskne longjmp(buf,1); // skočí zpět na místo, kde byl volán setjmp - takže setjmp nyní vrátí 1}prázdnota za prvé() { druhý(); printf("za prvé n"); // netiskne}int hlavní() { -li (!setjmp(buf)) za prvé(); // při spuštění setjmp vrátil 0 jiný // když longjmp skočí zpět, setjmp vrátí 1 printf("hlavní n"); // tiskne vrátit se 0;}
Po provedení výše uvedený program vypíše:
druhý hlavní
Všimněte si, že ačkoli za prvé()
zavolá podprogram, "za prvé
„nikdy není vytištěn.“hlavní
"vytiskne se jako podmíněné prohlášení if (! setjmp (buf))
se provede podruhé.
Zpracování výjimek
V tomto příkladu setjmp
se používá ke zpracování výjimek v hranatých závorkách, jako Snaž se
v některých dalších jazycích. Volání na longjmp
je analogický k a házet
příkaz, který umožňuje výjimce vrátit chybový stav přímo do setjmp
. Následující kód dodržuje 1999 ISO C standard a Single UNIX Specification vyvoláním setjmp
v omezeném rozsahu kontextů:[7]
- Jako podmínka k
-li
,přepínač
nebo iterační prohlášení - Jak je uvedeno výše ve spojení s jediným
!
nebo srovnání s celočíselnou konstantou - Jako prohlášení (s nepoužitou návratovou hodnotou)
Dodržování těchto pravidel může implementaci usnadnit vytvoření vyrovnávací paměti prostředí, což může být citlivá operace.[3] Obecnější použití setjmp
může způsobit nedefinované chování, například poškození lokálních proměnných; vyhovující kompilátoři a prostředí nejsou povinni chránit nebo dokonce varovat před takovým použitím. Trochu propracovanější idiomy jako např switch ((exception_type = setjmp (env))) {}
jsou běžné v literatuře a praxi a zůstávají relativně přenosné. Níže je uvedena jednoduchá vyhovující metodika, kde je spolu se stavovou vyrovnávací pamětí udržována další proměnná. Tato proměnná by mohla být rozpracována do struktury zahrnující samotný buffer.
V moderněji vypadajícím příkladu by obvyklý blok „try“ byl implementován jako setjmp (s nějakým přípravným kódem pro víceúrovňové skoky, jak je vidět na za prvé
), „throw“ jako longjmp s volitelným parametrem jako výjimkou a „catch“ jako „else“ blok pod „try“.
#zahrnout <setjmp.h>#zahrnout <stdio.h>#zahrnout <stdlib.h>#zahrnout <string.h>statický prázdnota za prvé();statický prázdnota druhý();/ * Použijte statickou proměnnou s rozsahem souboru pro zásobník výjimek, abychom měli přístup * kdekoli v této překladové jednotce. * /statický jmp_buf výjimka_env;statický int typ_výjimky;int hlavní(prázdnota) { char* nestálý mem_buffer = NULA; -li (setjmp(výjimka_env)) { // pokud se sem dostaneme, došlo k výjimce printf("nejprve selhalo, typ výjimky:% d n", typ_výjimky); } jiný { // Spustit kód, který může signalizovat selhání prostřednictvím longjmp. uvádí("volání první"); za prvé(); mem_buffer = malloc(300); // přidělit zdroj printf("% s n", strcpy(mem_buffer, „první úspěšné“)); // nedosaženo } volný, uvolnit(mem_buffer); // NULL lze předat zdarma, neprovádí se žádná operace vrátit se 0;}statický prázdnota za prvé() { jmp_buf my_env; uvádí("vstupující první"); // dosáhla memcpy(my_env, výjimka_env, velikost my_env); přepínač (setjmp(výjimka_env)) { případ 3: // pokud se sem dostaneme, došlo k výjimce. uvádí("druhý selhal, typ výjimky: 3; přemapování na typ 1"); typ_výjimky = 1; výchozí: // propadnout memcpy(výjimka_env, my_env, velikost výjimka_env); // obnovit zásobník výjimek longjmp(výjimka_env, typ_výjimky); // pokračovat ve zpracování výjimky případ 0: // normální, požadovaná operace uvádí("volá druhý"); // dosáhla druhý(); uvádí(„druhý úspěšný“); // nedosaženo } memcpy(výjimka_env, my_env, velikost výjimka_env); // obnovit zásobník výjimek uvádí("odcházející první"); // nikdy nedosaženo}statický prázdnota druhý() { uvádí(„zadávání druhého“ ); // dosáhla typ_výjimky = 3; longjmp(výjimka_env, typ_výjimky); // prohlásit, že program selhal uvádí(„opouštět druhý“); // nedosaženo}
Výstupem tohoto programu je:
volání firstentering firstcalling secondentering secondsecond se nezdařilo, typ výjimky: 3; přemapování na typ 1 první selhalo, typ výjimky: 1
Ačkoliv typ_výjimky
Proměnná zde není technicky nutná, protože setjmp vrací to, s čím byla nenulová hodnota longjmp volána (jako ve druhém a prvním), v praxi by se pro přizpůsobení bohatších výjimek použil propracovanější globální objekt.
Ve skutečném světě byl setjmp-longjmp (sjlj) standardním způsobem zpracování výjimek v kompilátorech Windows C ++ třetích stran (jmenovitě MinGW ), protože nativní Strukturované zpracování výjimek je obecně špatně zdokumentován a do roku 2014 byl také patentován na 32bitových Windows.
Kooperativní multitasking
C99 stanoví, že longjmp
je zaručeno, že bude fungovat pouze v případě, že cílem je volací funkce, tj. že je zaručeno, že bude cílový obor neporušený. Přechod na funkci, která již byla ukončena vrátit se
nebo longjmp
není definováno.[6] Většina implementací longjmp
při provádění skoku konkrétně neničte místní proměnné. Protože kontext přežívá, dokud nejsou vymazány jeho lokální proměnné, mohl by jej ve skutečnosti obnovit setjmp
. V mnoha prostředích (např Opravdu jednoduchá vlákna a TinyTimbers ), idiomy jako if (! setjmp (child_env)) longjmp (caller_env);
může umožnit volané funkci efektivně pozastavit a obnovit v a setjmp
.
Toto využívají knihovny vláken k poskytování kooperativní multitasking zařízení bez použití setcontext
nebo jiný vlákno zařízení. Zatímco setcontext
je služba knihovny, která může vytvořit kontext spuštění v paměti přidělené haldě a může podporovat další služby, jako je ochrana proti přetečení vyrovnávací paměti,[Citace je zapotřebí ] zneužívání setjmp
je implementován programátorem, který si může vyhradit paměť na zásobníku a neoznámit knihovně nebo operačnímu systému nový operační kontext. Na druhou stranu implementace knihovny z setcontext
může interně používat setjmp
podobným způsobem jako v tomto příkladu k uložení a obnovení kontextu poté, co byl nějak inicializován.
Vezmeme-li v úvahu, že setjmp
dětská funkce bude obecně fungovat, pokud nebude sabotována, a setcontext
, jako část POSIX, nemusí být poskytovány implementacemi C, tento mechanismus může být přenosný, pokud setcontext
alternativa selže.
Vzhledem k tomu, že při přetečení jednoho z více zásobníků v takovém mechanismu nebude vygenerována žádná výjimka, je nezbytné přecenit prostor požadovaný pro každý kontext, včetně toho, který obsahuje hlavní()
a včetně prostoru pro všechny obsluhy signálů, které by mohly přerušit pravidelné provádění. Překročení přiděleného prostoru poškodí ostatní kontexty, obvykle s vnějšími funkcemi jako první. Systémy vyžadující tento druh programovací strategie jsou bohužel často také malé s omezenými zdroji.
#zahrnout <setjmp.h>#zahrnout <stdio.h>jmp_buf hlavní úkol, childTask;prázdnota call_with_cushion();prázdnota dítě();int hlavní() { -li (!setjmp(hlavní úkol)) { call_with_cushion(); // dítě se nikdy nevrátí, výnos } // spuštění pokračuje po tomto „}“ po prvním výnosu dítěte zatímco (1) { printf("Rodič n"); -li (!setjmp(hlavní úkol)) longjmp(childTask, 1); // výnos - všimněte si, že v C99 to není definováno }}prázdnota call_with_cushion() { char prostor[1000]; // Vyhrazte si dostatek místa pro chod main prostor[999] = 1; // Neoptimalizujte pole mimo existenci dítě();}prázdnota dítě() { zatímco (1) { printf(„Dětská smyčka začíná n"); -li (!setjmp(childTask)) longjmp(hlavní úkol, 1); // výnos - zneplatní childTask v C99 printf("Konec dětské smyčky n"); -li (!setjmp(childTask)) longjmp(hlavní úkol, 1); // výnos - zneplatní childTask v C99 } / * Nevracejte se. Místo toho bychom měli nastavit příznak označující, že main () by se měl přestat poddávat nám a pak longjmp (mainTask, 1) * /}
Viz také
Reference
- ^ A b ISO C to uvádí
setjmp
musí být implementováno jako makro, ale POSIX výslovně uvádí, že není definováno, zdasetjmp
je makro nebo funkce. - ^ Toto je typ, který používá Knihovna GNU C., verze 2.7
- ^ A b Odůvodnění C99, verze 5.10, duben 2003, oddíl 7.13
- ^ Přednáškové poznámky k CS360 - Setjmp a Longjmp
- ^ setjmp (3) Archivováno 2009-07-26 na Wayback Machine
- ^ A b ISO / IEC 9899: 1999, 2005, 7.13.2.1:2 a poznámka pod čarou 211
- ^ Specifikace Single UNIX, Vydání 7 od Otevřená skupina : nastavit skokový bod pro ne-místní goto - System Interfaces Reference,
externí odkazy
- Specifikace Single UNIX, Vydání 7 od Otevřená skupina : nastavit skokový bod pro ne-místní goto - System Interfaces Reference,
- Výjimky v C s Longjmp a Setjmp
- existuje sigsetjmp / siglongjmp (znovu) (o této funkci v mingw /MSYS )