Hello, This question is not covered in the C language programming textbooks. It would be interesting to know why and how the setjmp and longjmp functions are used. If possible, please give some example of their use. Thank.
3 answers
An example of using longjmp from my real life. I made a fairly voluminous utility for testing the piece of iron that we produce. There, in particular, was a memory test. It was required that the testing be carried out before pressing Ctrl / C, after which the transition to the next stage of testing was carried out. Did this:
// ΠΠ°Π΄Π°ΡΠΌ ΡΠ΅Π°ΠΊΡΠΈΡ Π½Π° Π½Π°ΠΆΠ°ΡΠΈΠ΅ ΠΊΠΎΠΌΠ±ΠΈΠ½Π°ΡΠΈΠΈ ΠΊΠ»Π°Π²ΠΈΡ Π‘trl/C signal(SIGINT, sig_sigint); . . . // ΠΠ΅ΡΠ΅Π΄ Π½Π°ΡΠ°Π»ΠΎΠΌ ΡΠΈΠΊΠ»Π° Π·Π°ΠΏΠΎΠΌΠΈΠ½Π°Π΅ΠΌ ΠΏΠΎΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅: if (setjmp(jmpbuffer) != 0) { // ΠΠ»ΠΈΠ½Π½ΡΠΉ ΠΏΠ΅ΡΠ΅Ρ
ΠΎΠ΄ ΠΈΠ· ΠΎΠ±ΡΠ°Π±ΠΎΡΡΠΈΠΊΠ° ΡΠΈΠ³Π½Π°Π»Π° Π·Π°Π²Π΅ΡΡΠ΅Π½ΠΈΡ printf("\n\nΠΠΎΠ»ΡΡΠ΅Π½ ΡΠΈΠ³Π½Π°Π» Π·Π°Π²Π΅ΡΡΠ΅Π½ΠΈΡ ΡΠ°Π±ΠΎΡΡ\n"); goto end_loop; } // Π‘Π°ΠΌ ΡΠΈΠΊΠ» ΡΠ΅ΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΡ do { . . . } while (do_work !=0); end_loop: There were several such stages of testing. And after each pressing of Ctrl / C, the program proceeded to the NEXT stage. Try it on try / catch! :-) And do not talk about the goto operator! In this context, with the transition forward, its use is absolutely appropriate.
The setjmp and longjmp functions are used almost for the same purpose, for which the new languages ββhave try/catch and throw constructions. The latter can call setjmp and longjmp in some implementations, at least in C ++.
Consider the following case of calling functions in a program: f1 calls f2 , and that in turn f3 , which calls itself 100 times recursively. Parameters, return addresses and local variables of each launched function will fall onto the stack. All this together is called the stack frame , and in our case 102 frames will be added to the stack.
The return address is the physical address of the instruction that must be executed next after the completion of the called function. This address is stored on the stack when the machine instruction is executed, which is called something like CALL and is restored when the instruction is executed, which is called something like RET .
The usual way to return to the f1 function from the nested function itself is that all functions are completed in the standard way, and they remove their frames from the stack one by one. This, as you know, is long. In the case of an exceptional situation, I would like to immediately go upstairs to where the global handling of exceptional situations occurs.
The normal way to do this is impossible, but in the old days, programmers came up with a fairly simple hack. The stack is just a memory, and the pointer to the top of the stack is one of the registers of the processor. When adding values ββto the stack, everything that was there before remains unchanged, therefore, in order to restore the stack, it is enough just to return the pointer value to the one that, for example, the function f1 .
But in order to return it, you must first remember it. In fact, you also need to remember the pointer of the current instruction, because when you return, you must not only restore the stack, but also transfer control to the instruction inside the f1 function.
To do this, use the setjmp function:
#define TOO_DEEP_RECURSION 1 jmp_buf env; int main() { int val; val = setjmp(env); if(val) { fprintf(stderr,"ΠΠΎΠ·Π½ΠΈΠΊΠ»Π° ΠΎΡΠΈΠ±ΠΊΠ° Ρ ΠΊΠΎΠ΄ΠΎΠΌ %d",val); return val; } recursive100(1); return 0; } recursive100(int level) { if (level == 100) longjmp(env, TOO_DEEP_RECURSION); recursive100(level + 1); } Since this is a hack, the code looks a bit strange, but you need to understand the principle. The first time you call setjmp function stores all the necessary pointers in the jmp_buf structure and returns 0. As you can see, in this case, the program continues in the standard way and it calls 100 functions recursively.
With the 100th call, we go back using longjmp . This function takes a structure as an input, extracts from it a pointer to the next instruction and a pointer to the top of the stack, and restores them. The second parameter is an integer, which should not be 0.
The control is passed back to main as if it had just returned from setjmp and the function returns the integer you passed to longjmp . This time it is not equal to 0, the function main displays an error message and stops.
There can be several longjmp functions in your program, each can return its own code, and based on the code, you can display different messages.
Naturally, you can create several jmp_buf variables, and organize complex return logic. It is important to remember two things:
Before calling
longjmpthejmp_bufstructure must be initialized viasetjmp, otherwise the programβs behavior will be unpredictable.With a quick return, only the stack is cleaned. There is no code to release other resources, such as freeing memory from a heap, closing files, closing sockets, and so on.
- Thanks, clearly written. But you need to be careful not to transfer control to a non-existent context. - Vyacheslav
- @ Vyacheslav Yes, of course,
longjmpwithout priorsetjmpguaranteed to drop the program. - Mark Shevchenko
My version:
typedef enum { NoException = 0, Exception, ArgumentException, NotImplementedException, /* ... */ } __exception_types; #define try /* ... */ #define catch(x) /* ... */ #define throw(x,...) /* ... */ #define finally /* ... */ Using:
try { puts("throw(NotImplementedException) call..."); throw(NotImplementedException, "in future releases only"); } catch( NotImplementedException ) { printf("catch NotImplementedException" ); if( __exeption_with_msg ) { printf( ": %s", __exeption_msg ); } printf( "\n" ); } else { printf("exception %d)", __exception_type ); if( __exeption_with_msg ) { printf( ": %s", __exeption_msg ); } printf( "\n" ); } finally { puts( "finally() block" ); } Define the content I propose to come up with yourself :)
- Apparently everyone does. But this is not as easy as in ++! - Vyacheslav
- @ Vyacheslav, who are these all? When I came up with my own version I found only two, well, two and a half, decent implementations :) I think, because in fact this is not really needed by anyone. Me too :) - PinkTux
- Professionals ... - Vyacheslav
- one@ Vyacheslav, their application is always the same: return to some state with the restoration of the context. For example . And the examples have already shown what they do not like? Or are they not classical enough for you? So I assure you, there will be no other examples in principle. By the way, what do you mean by "coroutines" and why consider them, along with generators, archaic nonsense? - PinkTux
- one@ Vyacheslav, you yourself are determined, you need coroutines or it is archaic :-) - PinkTux