break 与 goto:在 C 中的以下 bool 函数中哪个更合适?

问题描述

我编写了以下函数,用于检查字符串中是否存在任何非数字字符。该函数必须在找到任何非数字字符后立即停止并退出循环并返回 true。否则,它将返回false。这是我正在使用的代码:(包含 ctype.h 库)。

bool isnotdigit(string argv)
{
    bool y = false;
    for (int i = 0,n = strlen(argv); i < n; i++)
    {
        char c = argv[i];
        if (! isdigit(c))
        {
            y = true;
            break;
        }
    }
    return y;
}

也可以这样做:

bool isnotdigit(string argv)
{
    bool y = false;
    for (int i = 0,n = strlen(argv); i < n; i++)
    {
        char c = argv[i];
        if (! isdigit(c))
        {
            y = true;
            goto next;
        }
    }
next:
    return y;
}

如果我没记错的话,两个代码的工作方式是一样的。对?那么,两者的优缺点是什么?特别是在上面的 bool 函数的情况下。

解决方法

这是一个编程风格的问题,因此,您可能不会得到明确的答案。

有人说break只是伪装的goto,所以一个和另一个一样糟糕。有人说你永远不应该使用 break

break 的全部意义在于它是一个保证不会混淆的 goto。它总是循环内部,循环外部。 (当然,除非它是 switch 语句的一部分。)

在这种情况下,goto 给您带来的好处很少或没有。所以我认为没有人会说你的第二个例子比你的第一个好得多。 (当存在嵌套循环时,事情会变得更复杂。稍后会详细介绍。)

最后,另一种编写函数的方式是这样的:

bool isnotdigit(string argv)
{
    for (int i = 0,n = strlen(argv); i < n; i++)
    {
        char c = argv[i];
        if (! isdigit(c))
        {
            return false;
        }
    }
    return true;
}

但是有些人说this 是不好的风格,因为(他们说)一个函数应该只有一个return 语句。然而,其他人说这是好的风格,因为它摆脱了额外的布尔变量 y,并摆脱了额外的变量(特别是额外的小布尔变量,只是跟踪事物)也是一个很好的规则。

以下是我的看法:

  • goto 可能会令人困惑,您几乎不需要它。我几乎从不使用 goto
  • break 没问题。它几乎从不令人困惑。我用它所有的时间。所以我更喜欢你的第一个片段而不是你的第二个片段。
  • 额外的布尔变量,只是跟踪一些小事情,可能会令人困惑,尤其是当它们存在的唯一原因是为了绕过一些其他“规则”时。
  • 我对多个 return 语句没有任何问题,因此我总是会使用更类似于第三种选择的方法,正如我在本答案中所介绍的那样。

另见this older question

现在,公平地说,反对多个 return 语句的论点可能是一个很好的论点。 如果在退出之前你有清理工作要做,或者如果你以后不得不添加一些清理代码,很容易忘记在两个地方(或三个地方)添加它或更多地方)如果有多个 return 语句。对我来说,当函数小而简单时(就像在这种情况下一样),我认为清洁度(以及额外布尔变量的损失)大于风险。但是如果你的元风格是你不喜欢判断调用,如果你喜欢严格的规则,你可以在任何地方应用以避免表面风险,那么“没有多个return语句”规则是有道理的。

最后,还有嵌套循环的问题。对于第二个示例中 goto next; 的用法,我能想象的唯一理由是 如果 后来的程序员出现并添加了一个嵌套循环,则带有 break 的代码可能不会不再工作,并且必须以某种方式返工。通过使用 goto,合理化可能会发生,代码对这种可能性更加健壮。 (就我个人而言,我认为这不是对 goto 的一个很好的合理化,但正如我所说,这是我唯一能想到的。)

一旦您有了嵌套循环,优缺点(即 gotobreak 与多个 return 语句)肯定会发生变化,如对 {{3 }}。

附言一些语言通过使用类似 break(2) 的东西来“解决”嵌套循环中断问题,它可以一次中断多个循环。 C 没有这个,原因是它被认为太有可能令人困惑,而且如果后来的维护程序员添加或删除了一层嵌套,它很可能会破坏。

或者换句话说,尽管单级-break 是保证不会混淆的goto,但多级-break 可能与{{ 1}}。而且,当然,您可以在这里对我的描述提出异议:毕竟,谁说 goto 保证不会混淆?当然,不能严格保证,但这是因为任何语言功能如果使用不当都会令人困惑。 (举个例子:额外的小布尔变量,只是跟踪各种事情。)

,

它们在功能上是等效的,但我更愿意只使用 goto 来跳出嵌套循环,而不是单个循环。

goto 经常被人反对,因为它可以导致“spaghetti code”,但它在 C 中有它的位置:

  • 彻底摆脱嵌套循环

  • 资源释放,如下:

      char *buffer1 = NULL,*buffer2 = NULL,*buffer3 = NULL;
    
      buffer1 = malloc(1000);
      if(NULL == buffer1)
      {
          goto cleanup;
      }
    
      buffer2 = malloc(1000);
      if(NULL == buffer2)
      {
          goto cleanup;
      }
    
      buffer3 = malloc(1000);
      if(NULL == buffer3)
      {
          goto cleanup;
      }
    
      use(buffer1,buffer2,buffer3);
    
    cleanup:
    
      if(buffer1 != NULL)
      {
          free(buffer1);
          buffer1 = NULL;
      }
    
      if(buffer2 != NULL)
      {
          free(buffer2);
          buffer2 = NULL;
      }
    
      if(buffer3 != NULL)
      {
          free(buffer3);
          buffer3 = NULL;
      }
    
,

如果有其他选择,请不要使用 goto

但是在这个函数中你不需要,只要结果明确时return

bool isnotdigit(string argv)
{
    for (int i = 0,n = strlen(argv); i < n; i++)
    {
        char c = argv[i];
        if (! isdigit(c))
        {
            return true;
        }
    }
    return false;
}
,

至少在您示例中的简单情况下,我建议避免同时使用 gotobreak,而是将条件添加到 for 语句中。 (这可能是个人偏好或可能适用于您的项目的编码指南的问题。)

bool isnotdigit(string argv)
{
    bool y = false;
    for (int i = 0,n = strlen(argv); (i < n) && !y; i++)
    {
        char c = argv[i];

        /* or alternatively: y = ! isdigit(c); */
        if (! isdigit(c))
        {
            y = true;
        }
    }

    return y;
}

顺便说一句:我建议重命名函数参数,因为 argv 可能会与 main 函数的标准参数混淆,后者通常称为 argcargv。而不是 y 我会建议一个有意义的名字,例如wrong_char_found

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...