从嵌入式C/C++逻辑中消除goto语句

4

我想要摆脱这个goto语句。有没有人能告诉我同样的逻辑?下面的代码不是我要修改的确切代码,但它将支持我的问题。请在评论此帖时不要问代码的意义,因为它只是一个例子。

int result[i][j];
for (int i = 0; i<100; i++)
{
    for (j = 0; j<100; j++)
    {
        result[i][j] = j++;
        if (j == 80)
            goto break1;
    }
}
break1:
…;

6
请指定语言,C和C++具有不同的习惯用语和不同的解决方案。 - Mgetz
2
goto 不一定是邪恶的。在复杂的流程控制循环中,它可能是最有效的方法。 - Joe
使用可以在每个循环级别询问的变量是一种常见的解决方案。在我的观点中,goto更清晰地执行它应该执行的操作:离开循环!首先应该做的是简化嵌套循环,但如果这也不是一个有用的方法,请使用简单、易于理解和清晰的goto! - Klaus
在这个例子中,你可以将内部循环改为 for (j = 0; j < 80; j++)。问题的更一般版本是什么?它可能是 if (j == function(i, j)) goto break1; 吗?如果是这样,那么 goto 是合理的。否则,你必须使用一个标志和一个 break 语句。(顺便说一下,goto 不是一个“调用”;它是一个语句。) - Jonathan Leffler
1
可能应该将 int result[i][j]; 改为 int result[100][100]; - Keith Thompson
显示剩余6条评论
7个回答

4
将这些循环放在一个函数中,给它取一个适当的名字,并在完成后使用return;。如果需要两个循环才能完成,则它应该有一个名字。
完成标志太难读了,你应该将这个结构放在它自己的函数中,使它过时。
异常只用于无法在本地处理的错误。使用它们来通知更高级别的函数发生了你无法修复的问题,而不是发生了应该发生的事情。

MISRA也不会容忍嵌入式的return语句。他们更倾向于使用一个完成标志。我同意你的看法,这将是一个可怕的解决方案,修复比问题更糟糕。 - chqrlie
@chqrlie 我不知道MISRA,但如果它偏爱标志而不是函数,我认为它肯定很愚蠢。什么样的系统不支持return语句呢? - Baum mit Augen
他们坚持在函数体的末尾只有一个return语句。 - chqrlie

2
我认为有三种可能的解决方案。
  1. 将代码放入一个函数中,并使用return离开该函数。
  2. 使用“完成”标志,就像Michel Keijzers、Bas in het Feld和EvilTeach在答案中很好地演示的那样。
  3. (仅限于C++)用try-catch块包围代码段,在想要退出代码时抛出异常。但请记住,异常通常应该用于错误处理。因此,只有在终止循环是错误条件的结果时才应使用此模式。

7
例外?真的吗? - Javi
2
OP要求嵌入式代码。通常,嵌入式系统会关闭异常和RTTI。我认为异常不应该用于处理“正常”的中止条件。 - Klaus
1
使用异常处理这个问题甚至更加恶劣! - chqrlie
异常应该只用于...异常情况,而在这种情况下似乎(或可能)发现了不是异常的东西。 - Michel Keijzers

1
使用布尔值来跳出 for 循环。
int result[i][j];
bool cont = 1;
for (int i =0;i<100;i++)
{
    for(j = 0;j<100;j++)
    {
        result[i][j] = j++;
        if(j == 80)
        {
            cont = 0;            
            break; 
        }
    }
    if (cont == 0)
        break;
}
break1;

(注意:未在实际编译器上进行测试。)

1
使用goto的代码对我来说很容易阅读。如果你的修改是解决方案,那我就要把我的问题拿回来 :-) - Klaus
如果真实代码中有 continue,这将失败。 - chqrlie

1

由于您想要中断两个循环,因此必须通知外部循环。您可以通过使用一个布尔值来检查这一点来实现:

bool break_loop = false;
for (int i = 0; i < 100; ++i) {
    for (int j = 0; j < 100; ++j) {
        if (j == 80) {
            break_loop = true;
            break;
        }
    }
    if (break_loop) break;
}

0
int result[i][j];
for (int i = 0; i<100; i++)
{
    for (j = 0; j<100; j++)
    {
        result[i][j] = j++;
        if (j == 80)
        {
           i = 100;
           break;
        }
    }
}

break语句会跳出内部循环。 将i设置为100会导致外部循环结束。

break语句是否符合MISRA编译标准? - studinstru

0

我有时候喜欢改变控制变量

for (int i = 0; i < 100; i++) {
    for (int j = 0; j < 100; j++) {
        /* work */
        if (j == 80) i = j = 100; // 100 makes both loops terminate
    }
}

我们已经看到了十几种解决goto的方案。但是我相信,在所有这些“解决方案”中,goto甚至比所有代码都要好。正如我们所看到的,“goto很糟糕”可能会导致更糟糕的代码。在工作中,我们有编码规则,禁止在循环本身内修改控制/循环变量。我认为这和goto一样糟糕。 - Klaus

0
int result[i][j];
for (int i = 0; i<100; i++)
{
    int j;
    for (j = 0; j<100; j++)
    {
        result[i][j] = j++;
        if (j == 80)break;
    }
    if(j == 80) break;
}

知道这是一个老问题,但可以简单地修改


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接