从该循环中调用的函数中断开循环

Mag*_*sch 29 c for-loop break

我正在试图找出一种方法来摆脱for在该循环中调用的函数内的循环.我知道有可能让函数返回一个值,然后检查一个特定的值然后中断,但我想直接从函数内部执行.这是因为我使用内部库来处理特定的硬件,要求我的函数的函数签名如下所示:

void foo (int passV, int aVal, long bVal)
Run Code Online (Sandbox Code Playgroud)

我知道不使用返回值是非常糟糕的做法,但是有很多情况迫使我这样做,所以请耐心等待.

考虑以下示例:

#include <stdio.h>

void foo (int a) {
    printf("a: %d", a);
    break;
}

int main(void) {
    for (int i = 0; i <= 100; i++) {
        foo(i);
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

现在这不编译.相反,我得到一个编译错误如下:

prog.c:在函数'foo'中:prog.c:6:2:错误:break语句不在循环或switch break中;

我知道这意味着什么(编译器说中断foo()不在for循环中)

现在,我可以从关于break语句的标准中找到的是:

break语句使控制权传递给最内部封闭while,do,for或switch语句之后的语句.语法只是简单;

考虑到我的函数是从for循环中调用的,为什么break语句不能说出for循环?此外,是否有可能在不首先返回功能的情况下实现这样的功能?

chq*_*lie 44

你不能用break;这种方式,它必须出现在for循环体内.

有几种方法可以做到这一点,但不推荐:

  • 您可以使用该exit()功能退出程序.由于循环是从它运行的,main()并且你之后没有做任何事情,所以可以通过这种方式实现你想要的东西,但它是一个特例.

  • 您可以在函数中设置全局变量,并for在函数调用后在循环中测试它.通常不建议使用全局变量.

  • 你可以使用setjmp()和longjmp(),但这就像试图用锤子击打苍蝇,你可能会破坏其他东西并完全错过苍蝇.我不推荐这种方法.此外,它需要jmpbuf您必须传递给函数或作为全局变量访问.

可接受的替代方法是将status变量的地址作为额外参数传递:函数可以将其设置为指示需要从循环中断.

但到目前为止,C中最好的方法是返回一个值来测试延续,它是最可读的.

根据你的解释,你没有源代码,foo()但可以检测一个你可以直接或间接调用的函数中的某些条件foo():longjmp()将从它的位置跳出,深入内部foo(),可能是调用堆栈的多个层次到该setjmp()位置,绕过所有中间呼叫的常规功能退出代码.如果这正是您需要做的以避免崩溃,setjmp()/ longjmp()是一个解决方案,但它可能会导致其他问题,如资源泄漏,缺少初始化,不一致状态和其他未定义行为的来源.

请注意,您的for循环将迭代101次数,因为您使用<=运算符.惯用for循环用于for (int i = 0; i < 100; i++)精确迭代显示为上(排除)边界的次数.

  • 应该强调的是,评估返回值绝对是可行的方法,因为它可以产生最佳的可读性. (6认同)

Tho*_*thy 24

break,就像goto,只能在同一个函数中本地跳转,但如果你绝对必须,你可以使用setjmp和longjmp:

#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;
}
Run Code Online (Sandbox Code Playgroud)

呼叫longjmp将导致跳回setjmp呼叫.返回值setjmp显示是否在设置跳转目标后返回,或者是否从跳转返回.

输出:

Inside foo!
Jumped out!
Run Code Online (Sandbox Code Playgroud)

如果使用正确,非局部跳跃是安全的,但有许多事情需要仔细考虑:

  • 由于longjmp跳过"通过" setjmp调用和longjmp调用之间的所有函数激活,如果这些函数中的任何一个期望能够在执行当前位置之后执行额外的工作,那么将无法完成该工作.
  • 如果调用的函数激活setjmp已终止,则行为未定义.任何事情都可能发生.
  • 如果setjmp尚未调用,则jump_target未设置,并且行为未定义.
  • 调用的函数中的局部变量在setjmp某些条件下可能具有未定义的值.
  • 一个字:线程.
  • 其他内容,例如浮点状态标志可能不会被保留,并且您可以在何处进行setjmp调用.

如果你对机器指令和CPU寄存器的非本地跳转有很好的理解,那么大多数都会自然地遵循,但除非你有这个,并且已经阅读了C标准的功能并且不能保证,我会建议一些警告.

  • 可能值得添加一个大的"这通常是一个非常糟糕的主意"警告在这个答案之上. (22认同)
  • @Magisch:请注意,尽管你已经解决了对void-return的明确要求,但你可能仍然会破坏代码.如果你的函数"必须返回无效",那么你以前的同事的代码可能特别是在假设它"必须返回"的情况下编写的.但是你现在已经编写了一个不返回的函数,而是跳出来了.请仔细检查您的同事是否编写了您现在跳过的清理代码,因为如果是这样,那么跳过它可能会导致资源泄漏,或者使某些数据结构处于不一致状态. (9认同)
  • @Magisch:特别是在阅读了你的最新评论之后,我想在这个程序中仔细考虑一下longjumps.但如果这是唯一的方法,并且仔细测试未能发现任何问题,那么也许你可以交叉指责并希望最好.告诉我这将安装哪种类型的飞机,所以我知道要避开哪些航班...... (4认同)
  • @Magisch:我很好奇`setjmp` /`longjmp`与返回要检查的谷相比如何减少'重写'的必要性?使用`setjmp` /`longjmp`将需要修改调用站点和函数.在许多情况下添加返回值是相同的 - 除了只需要修改需要能够执行`break`的调用站点.其他呼叫站点可以继续调用该函数并忽略返回值. (2认同)
  • @Magisch我仍然不明白为什么你不能修改那个写得不好的部分以返回别的东西而不是void,但你可以修改它来引入setjmp/longjmp模糊. (2认同)
  • @Magisch每次调用[`printf`](http://www.cplusplus.com/reference/cstdio/printf/)时都会使用它,它返回一个`int`(写入的字符总数)但是几乎所有时间都忽略了.您将忽略函数的返回值_never_得到崩溃.这种行为是完美定义的. (2认同)
  • 这是一个可怕的,可怕的想法-1 (2认同)

Jac*_*ley 9

(注意:自从我最初编写此问题以来,该问题已被编辑)

由于编译C的方式,它必须知道在调用函数时中断的位置.既然你可以从任何地方调用它,甚至某个地方都没有意义,你就不能break;在你的函数中有一个声明并让它像这样工作.

其他答案提出了可怕的解决方案,例如设置全局变量,使用#define函数或跳转(!).这些都是极差的解决方案.相反,您应该使用在开头段落中错误地解除的解决方案,并从函数中返回一个值,该值指示break在这种情况下您要触发的状态并执行以下操作:

#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;
}
Run Code Online (Sandbox Code Playgroud)

试图找到模糊的方法来实现这样的事情而不是使用正确的方法来实现相同的最终结果是一种可靠的方法来生成可怕的质量代码,这是维护和调试的噩梦.

你提到,隐藏在注释中,你必须使用void返回,这不是问题,只需将break参数作为指针传递:

#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;
}
Run Code Online (Sandbox Code Playgroud)

由于您的参数是固定类型,我建议您使用强制转换将指针传递给其中一个参数,例如 foo(a, b, (long)&out);

  • 我特别讨论了我想在我的问题中使用返回值的替代方法,这还不够吗?如果你想让我的用例具有超特定性(对未来的访问者没有帮助,但是没问题),库要求函数的格式为`void foo(int passV,int aVal,long bVal)`doc for该库特别提到了这一点,并没有使用它使程序崩溃与segfault.我试过了. (2认同)
  • 我仍然认为静态全局变量比纠结指针要容易得多...... (2认同)

Zby*_*000 8

break是在编译期间解析的语句.因此,编译器必须在同一函数中找到适当的for/while循环.请注意,无法保证无法从其他位置调用该函数.


Fro*_*odo 6

如果无法使用该break指令,则可以在模块中定义局部变量,并在for循环中添加第二个运行条件.例如,如下代码:

#include <stdio.h>
#include <stdbool.h>

static bool continueLoop = true;

void foo (int a)
{
    bool doBreak = true;

    printf("a: %d",a);

    if(doBreak == true){
        continueLoop = false;
    }
    else {
        continueLoop = true;
    }
}
int main(void) {
    continueLoop = true;   // Has to be true before entering the loop
    for (int i = 0; (i <= 100) && continueLoop; i++)
    {
        foo(i);
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

请注意,在此示例中,这不完全是break-instruction,但for循环不会执行另一次迭代.如果你想做一个,break你必须插入一个if变量的条件continueLoop,导致break:

int main(void) {
    continueLoop = true;   // Has to be true before entering the loop
    for (int i = 0; i <= 100; i++)
    {
        foo(i);
        if(!continueLoop){
            break;
        }
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

  • 当然这是正确的答案.询问"你如何终止for循环?" `for(; CONDITION;)`就像问"你如何终止while循环?" `而(条件)`.注意大胆的CONDITION :) (2认同)