-4

As mentioned here: https://stackoverflow.com/a/58498330 when using setjmp and longjmp, and returning to the scope of setjmp, all accessible object have the same value as when the function longjmp is called.

I am confused about this, because longjmp takes us to a completely different place in the program, where the local variables when we call longjmp make zero sense. So how can the value be "the same" when the variables are all different?

possum
  • 1,837
  • 3
  • 9
  • 18
Mr User
  • 205
  • 1
  • 5
  • 4
    Do not tag both C and C++ except when asking questions about differences or interactions between the two languages. Keep the tag for the language you are using and delete the other tag. – Eric Postpischil May 06 '23 at 08:40
  • IIRC `longjmp` can only take you up the stack, so it can't bring into existence any variables that don't already exist, only destroy some. – HolyBlackCat May 06 '23 at 08:42
  • @EricPostpischil But in this case it is the same thing for C and C++ – Mr User May 06 '23 at 09:04
  • "all accessible object have the same value as when the function longjmp is called." thats not what the answer states. Its says "Upon return to the scope of setjmp, all accessible objects [...] have the same values as they had when std::longjmp was executed". Your question is missing context. – 463035818_is_not_an_ai May 06 '23 at 09:04
  • @463035818_is_not_a_number Then could you please help me clarify the question? – Mr User May 06 '23 at 09:05
  • @EricPostpischil Does not matter at all whether it is C or C++ – Mr User May 06 '23 at 09:07
  • I dont really understand what you are asking. I guess your question would be simpler if rather than refereing to an answer to question similar to yours, you state your question. An example may help. (and the other Q&A is useful context, but write your question such that it can be understood without reading the other Q&A) – 463035818_is_not_an_ai May 06 '23 at 09:09
  • Yes it does matter, because C and C++ are two different languages. – 463035818_is_not_an_ai May 06 '23 at 09:09
  • @463035818_is_not_a_number Oops I just realise that my first sentence is not complete. I have fixed that. – Mr User May 06 '23 at 09:25
  • you are still trying to reason about an answer given to a specific question without the context of that question. `longjmp` cannot take you anywhere. You should show a concrete example that you do not understand – 463035818_is_not_an_ai May 06 '23 at 09:30
  • ups typo. I meant `longjmp` can not take you everywhere. – 463035818_is_not_an_ai May 06 '23 at 09:36
  • 1
    Variables may exist even when they are not in scope. That seems to be your confusion. Executing `longjmp` brings different variables into scope, and those variables will have the same values as they did when the scope was different. You're probably reading too much into it, the quotation is just stating the obvious. – john May 06 '23 at 09:49
  • 1
    You may not care whether answers are given for C or C++, but participants here do. The policy for the tags is not to combine them except for questions about differences or interactions between the two languages. Sometimes answers differ for the languages even when the asker did not expect it. For people searching Stack Overflow in the future, we do not want a question to be tagged with C when an answer is only correct for C++ or vice versa. There are additional reasons to. Tagging both inappropriately is one reason a question will be voted down or voted to be closed. – Eric Postpischil May 06 '23 at 11:47

2 Answers2

2

The key word here is accessible. Local variables on the part of the stack that is unwound by calling longjmp cease to exist and are therefore no longer accessible. End of story.

It's worth pointing out that the destructors of those variables (if any) won't be called1. For this reason, don't use longjmp in C++ code. Use C++ exception handling instead, which unwinds the stack in a controlled way.

1I think MSVC does actually call them, but this is not something you should rely on if you want your code to be portable.

Paul Sanders
  • 24,133
  • 4
  • 26
  • 48
  • 1
    "destructors of those variables (if any) won't be called": It is actually undefined behavior (as far as the standard is concerned) if there is any variable with non-trivial destructor that _should_ have been called in the equivalent throw/catch constructor. (So even worse than it just being unspecified or implementation-defined whether they are called.) – user17732522 May 06 '23 at 14:42
  • @user17732522 That's true, I did read that. Hedging their bets a bit there, I would say. – Paul Sanders May 06 '23 at 15:42
2

There is a misunderstanding about what accessible variables are restored and unchanged.

The phrase in reference is:

Upon return to the scope of setjmp, all accessible objects, floating-point status flags, and other components of the abstract machine have the same values as they had when std::longjmp was executed, except for the non-volatile local variables in setjmp's scope, whose values are indeterminate if they have been changed since the setjmp invocation.

When longjmp is called with a jmp_buf previously set by a call to setjmp whose calling frame is still active in the call stack, part of processor state such as some of the registers and flags are restored from data saved in the jmp_buf and control is transferred back to the return address of the setjmp call, discarding all intermediary frames without calling any destructors or finalisation code. The only objects that are affected are non-volatile local variables in the setjmp scope, which may have been restored from the jmp_buf data or not, and thus have indeterminate values.

Here is an example:

#include <stdio.h>
#include <setjmp.h>

jmp_buf buf;
int v1;
volatile int v123 = 123, v456 = 456;

void willjmp(void) {
    longjmp(buf, 42);
}

int main(void) {
    volatile int v2;
    int val, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12;

    // initial values
    v1 = v123; v2 = v123; v3 = v123; v4 = v123;
    v5 = v123; v6 = v123; v7 = v123; v8 = v123;
    v9 = v123; v10 = v123; v11 = v123; v12 = v123;

    if ((val = setjmp(buf)) != 0) {
        printf("\nafter longjmp:\n");
        printf("val=%d\n", val);   // prints val=42
        printf("v1=%d\n", v1);     // prints v1=456
        printf("v2=%d\n", v2);     // prints v2=456
        // non volatile local variables have indeterminate values
        printf("v3..v12=%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
               v3, v4, v5, v6, v7, v8, v9, v10, v11, v12);
    } else {
        printf("initial values:\n");
        printf("val=%d\n", val);   // prints val=42
        printf("v1=%d\n", v1);     // prints v1=123
        printf("v2=%d\n", v2);     // prints v2=123
        // prints v3..v12=123,123,123,123,123,123,123,123,123,123
        printf("v3..v12=%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
               v3, v4, v5, v6, v7, v8, v9, v10, v11, v12);
        v1 = v456; v2 = v456; v3 = v456; v4 = v456;
        v5 = v456; v6 = v456; v7 = v456; v8 = v456;
        v9 = v456; v10 = v456; v11 = v456; v12 = v456;
        printf("\nbefore longjmp:\n");
        printf("v1=%d\n", v1);     // prints v1=456
        printf("v2=%d\n", v2);     // prints v2=456
        // prints v3..v12=456,456,456,456,456,456,456,456,456,456
        printf("v3..v12=%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
               v3, v4, v5, v6, v7, v8, v9, v10, v11, v12);
        willjmp();
    }
    return 0;
}

Output:

initial values:
val=0
v1=123
v2=123
v3..v12=123,123,123,123,123,123,123,123,123,123

before longjmp:
v1=456
v2=456
v3..v12=456,456,456,456,456,456,456,456,456,456

after longjmp:
val=42
v1=456
v2=456
v3..v12=123,123,123,123,123,123,123,123,123,123

Depending on the processor architecture, the compiler implementation and configuration, especially optimisation flags, the output for v3 through v16 could be 456 or 123 or possibly even something else. The output above was produced on my M2 laptop with optimisations enabled. With optimisations disabled, the last line becomes v3..v12=456,456,456,456,456,456,456,456,456,456.

As can be seen on Goldbolt's Compiler Explorer, different compilers produce different output, for example gcc -O outputs v3..v12=123,123,123,123,123,123,456,456,456,456

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • @StoryTeller-UnslanderMonica: I edited the answer again with a more telling example of indeterminate values. What did you change for the colorizer issue? – chqrlie May 06 '23 at 13:10
  • Just made the language choice explicit in the colorizer. With three backticks, either `c` or `lang-c` work the same. – StoryTeller - Unslander Monica May 06 '23 at 13:20
  • @StoryTeller-UnslanderMonica: thank you, I was not aware of the colorizer behavior when multiple language tags are specified. The diff in the history page shows the complete code block as *changed*. – chqrlie May 06 '23 at 16:02