Rectangle 27 0

c Breaking out of a loop from within a function called in that loop?


#include <stdio.h>

int leave = 0;

void foo (int a) {
    printf("a: %d", a);
    leave = 1;
}

int main(void) {
    for (int i = 0; i <= 100; i++) {
        foo(i);
        if (leave)
          break;
    }
    return 0;
}

@joe blow because the question states that the inner function declaration can't be changed to accommodate a return value. where is 'leave' declared in your one-liner?

Just set a global variable and check that on the loop:

sorry, I meant as a function, like i<101&&!leave() Anyway look - you're quite right, the question is odd

what possible reason would you have to make the variable global? for(blah; I<101 & !leave; ++i)

Note
Rectangle 27 0

c Breaking out of a loop from within a function called in that loop?


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

jmp_buf jump_target;

void foo(void)
{
    printf("Inside foo!\n");
    longjmp(jump_target, 1);
    printf("Still inside foo!\n");
}

int main(void) {
    if (setjmp(jump_target) == 0)
        foo();
    else
        printf("Jumped out!\n");
    return 0;
}
Inside foo!
Jumped out!
  • If setjmp hasn't yet been called, then jump_target is not set, and the behaviour is undefined.
  • If the function activation that called setjmp has terminated, the behaviour is undefined. Anything can happen.
  • Local variables in the function that called setjmp can under certain conditions have undefined values.
  • Other things, such as that floating-point status flags might not be retained, and that there are restrictions on where you can put the setjmp call.
  • Since longjmp jumps "through" all the function activations between the setjmp call and the longjmp call, if any of those functions expect to be able to do additional work after the current place in execution, that work will simply not be done.

@Magisch I still don't understand why you can't modify that badly written part to return something else than void, but you can modify it introducing the setjmp/longjmp fuzz.

@Magisch: I'm curious how setjmp/longjmp would reduce the 'rewrite' necessary as compared to returning a vale to be checked? Using setjmp/longjmp will require both the call site and the function to be modified. Adding a return value is the same in many cases - except that only call sites that need to be able to perform the break need to be modified. Other call sites can just continue to call the function and ignore the return value.

@Magisch: Especially having read your latest comment, I would want to be a bit careful stomping around with longjumps in this program. But if it's the only way, and careful testing fails to find any problems, then perhaps you can cross your fingers and hope for the best. Just tell me which type of airplane this will be installed in, so I know which flights to avoid...

@Magisch: beware that you might still be breaking the code despite the fact that you've worked around the explicit requirement for a void-return. If your function "must return void", then your former colleague's code might in particular have been written on the assumption that it "must return" at all. But you've now written a function that doesn't return, it jumps out instead. Check very carefully that your colleague didn't write clean-up code which you're now jumping past, because if so then skipping it likely will cause resource leaks, or leave some data structures in an inconsistent state.

Might be worth adding a big "This is usually a very bad idea" warning on top of this answer.

Most of these follow naturally if you have a good understanding of what a nonlocal jump does on the level of machine instructions and CPU registers, but unless you have that, and have read what the C standard does and does not guarantee, I would advise some caution.

Nonlocal jumps are safe when used correctly, but there are a number of things to think carefully about:

The call to longjmp will cause a jump back to the setjmp call. The return value from setjmp shows if it is returning after setting the jump target, or if it is returning from a jump.

break, like goto, can only jump locally within the same function, but if you absolutely have to, you can use setjmp and longjmp:

Note
Rectangle 27 0

c Breaking out of a loop from within a function called in that loop?


int foo (int a) {
    if(a == someCondition) return 0;
    else {
        printf("a: %d", a);
        return 1;
    }
}

int main(void) {
    for (int i = 0; i <= 100; i++) {
        if(!foo(i)) break;
    }
    return 0;
}

Also consider doing something like this:

Edit: I'm not explicitly against the use of goto, setjmp, longjmp etc. But I think in this case there is a much simpler and more concise solution available without resorting to these measures!

In a case like this consider using a while() loop with several conditional statements chained with && instead of a for loop. Although you can alter the normal control flow using functions like setjmp and longjmp, it's pretty much considered bad practice everywhere. You shouldn't have to search too hard on this site to find out why. ( In short it's because of it's capacity to create convoluted control flow that doesn't lend itself to either debugging or human comprehension )

In this case, the loop depends on a true value being returned from 'foo', which will break the loop if the condition inside 'foo' is not met.

Note
Rectangle 27 0

c Breaking out of a loop from within a function called in that loop?


But by far the best approach in C is returning a value to test for continuation

An acceptable alternative is to pass the address of a status variable as an extra argument: the function can set it to indicate the need to break from the loop.

From your explanations, you don't have the source code for foo() but can detect some conditions in a function that you can modify called directly or indirectly by foo(): longjmp() will jump from its location, deep inside the internals of foo(), possibly many levels down the call stack, to the setjmp() location, bypassing regular function exit code for all intermediary calls. If that's precisely what you need to do to avoid a crash, setjmp() / longjmp() is a solution, but it may cause other problems such as resource leakage, missing initialization, inconsistent state and other sources of undefined behavior.

It should be emphasized that evaluating a return value is absolutely the way to go, as it yields best readability.

Note that your for loop will iterate 101 times because you use the <= operator. The idiomatic for loop uses for (int i = 0; i < 100; i++) to iterate exactly the number of times that appears as the upper (excluded) bound.

There are several ways to do this, but neither is recommended:

You cannot use break; this way, it must appear inside the body of the for loop.

Note
Rectangle 27 0

c Breaking out of a loop from within a function called in that loop?


#include <stdbool.h>

bool checkAndDisplay(int n)
{
    printf("%d\n", n);
    return (n == 14);
}

int main(void) {
    for (int i = 0; i <= 100; i++) {
        if (checkAndDisplay(i))
            break;
    }
    return 0;
}
#include <stdbool.h>

void checkAndDisplay(int n, bool* wantBreak)
{
    printf("%d\n", n);
    if (n == 14)
        wantBreak = true;
}

int main(void) {
    bool wantBreak = false;
    for (int i = 0; i <= 100; i++) {
        checkAndDisplay(i, &wantBreak);
        if (wantBreak)
            break;
    }
    return 0;
}

(Note: the question has been edited since I originally wrote this)

Because of the way C is compiled it must know where to break to when the function is called. Since you can call it from anywhere, or even somewhere a break makes no sense, you cannot have a break; statement in your function and have it work like this.

I specifically discuss that I want an alternative to using return values in my question, is that not enough? If you want to get hyper-specific to my use case (not helpful for future visitors imo but ok) the function is mandated by the library to have the format void foo (int passV, int aVal, long bVal) The doc for the library specifically mentions this and not using it makes the program crash with a segfault. I've tried it.

It's helpful if you include the constraint in your question rather than hiding it in a comment. I have added a section about having a void return.

No, because you don't state it is a requirement; it just sounds like you're ignorant.

Other answers have suggested terrible solutions such as setting a global variable, using a #define or longjumping(!) out of the function. These are extremely poor solutions. Instead, you should use the solution you wrongly dismiss in your opening paragraph and return a value from your function that indicates the state that you want to trigger a break in this case and do something like this:

Since your parameters are fixed type I suggest you use a cast to pass in the pointer to one of the parameters, e.g. foo(a, b, (long)&out);

The data types are mandated. Are you suggesting I misuse one of my parameters to pass a pointer instead? I may be able to do that if I work two of my parameters into one using a bit of bitshifting and then untangle them from within the function.

Trying to find obscure ways to achieve things like this instead of using the correct way to achieve the same end result is a surefire way to generate dire quality code that is a nightmare to maintain and debug.

You mention, hidden in a comment, that you must use a void return, this is not a problem, just pass the break parameter in as a pointer:

Note