【问题标题】:C usage of gotogoto的C用法
【发布时间】:2020-08-17 17:54:24
【问题描述】:

我有一个带有某种回调机制的状态机,函数由任务运行程序调用。这一切的背后是一个状态机,它有4种状态,有的相互排斥,有的可以组合,形成了相当复杂的规则集。如果尝试任何非法操作,其中一项功能应向用户显示错误消息(为简单起见,printf 此处):

static int state1 = 0;
static bool switch2 = 1;

void do_stuff(int value){
    int errorCode = 0;
    if(state1 == 1){ 
        errorCode = -1;
        goto ERROR;
    }
    if(state1 == 2 && switch2)
    {
        errorCode = 2;
        goto ERROR;
    }


    printf("No error!");
    return;
ERROR:
    printf("%d", errorCode);
}

这是我能想到的最短、最简洁的方法,但它一直被认为使用goto 是一件坏事。有没有更好的方法来解决这个问题,或者在稳定性和维护方面这是最好的方法吗?

【问题讨论】:

  • Gotos 完全可以用于实现状态机(为了掩护而躲避和奔跑)。但是你的代码是状态变量和 goto 的混合体,我不喜欢这样。
  • 您不仅在这里滥用goto,而且您所做的只是制造了一个简陋的异常版本。定义您自己的异常类,其中包括您的 errorCode 值和 throw
  • @EdHeal 我重新标记了这个问题,因为代码不是 C++
  • “使用 goto 是一件坏事”是一个可悲的普遍的过度概括。 C 中总是有替代方案,尤其是在 C++ 中。 通常有更好的选择,尤其是在 C++ 中。然而,在“通常”和“总是”之间的差距中,在某些情况下,谨慎、战略性地使用 goto 是最干净、最清晰的替代方案。拒绝可用的最干净、最清晰的代码,因为教条主义认为goto 是坏的、坏的、坏的!不是一个理性的决定。

标签: c goto


【解决方案1】:

goto很少是控制流的正确解决方案。虽然goto 有有效的用例,但在这个特定的函数中,您可以简单地将控制流重组为if-else 分支,如下所示:

void do_stuff(int value)
{
    int errorCode = 0;

    if (state1 == 1)
    { 
        errorCode = -1;
    }
    else if (state1 == 2 && switch2)
    {
        errorCode = 2;
    }
    else // unconditional case for no errors
    {
        printf("No error!");
        return;
    }
    printf("%s", errorCode);  // if control reaches here, print the error
}

【讨论】:

  • 他可以。但请指出goto 的问题。我没有看到太大的改进,也没有看到任何反对 goto 的有力论据。
  • @Cheatah 我的回答只是理所当然地认为goto 是有害的(就像问题一样)。我正在回答这个问题,“有更好的方法吗?”另外,就个人而言,我认为不使用 goto 的一项重大改进。通常,我觉得有责任证明goto 的使用是合理的,而不是相反。
  • @cigien 它是双向的。有时,您会在代码中看到体操,试图不惜一切代价避免goto,使用goto 可以简化它。所以它通常取决于手头的具体用例,而不是需要证明一种或另一种方式。
  • @P.P 够公平的。我倾向于在一方面犯错,但我明白你的意思。至少在这种情况下,似乎没有理由使用 goto,而且替代方案是完全可读的。
  • 这个解决方案很复杂,只是看起来有点干净,因为在每个错误情况下都没有清理代码。
【解决方案2】:

据我所知,您使用goto 进行错误处理,而不是状态机。

使用goto 进行错误处理实际上是其中一种非常有用和首选的情况,而不是复杂的条件链。它允许您在不重复代码的情况下执行手动 RAII:

int do_stuff(...)
{
    ... = f1(...);
    if (...)
        goto ERROR_f1;

    ... = f2(...);
    if (...)
        goto ERROR_f2;

    ... = f3(...);
    if (...)
        goto ERROR_f3;

    // Success
    return ...;

ERROR_f3:
    undo_f2(...);

ERROR_f2:
    undo_f1(...);

ERROR_f1:
    return ...;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-01
    相关资源
    最近更新 更多