【问题标题】:Styles of dealing with errors in C? [duplicate]C 中处理错误的方式? [复制]
【发布时间】:2011-08-25 16:17:00
【问题描述】:

可能重复:
Error handling in C code

大家好。我在一些小项目中使用 C,我看到了,因为它没有专门的错误处理结构,我不得不用额外的条件块来污染我的算法。我的问题是你更喜欢如何处理错误,以及为什么。我在两种方式之间左右为难......如果你有第三种方式,请发布。谢谢。

///////////////////////////////////////////
// method 1

// stuff that can go wrong;

if (test1 == failed)
{
    // print error;
    // exit;
}
else
{
    // more stuff that can go wrong;

    if (test2 == failed)
    {
        // print error;
        // exit;
    }
    else
    {
        // ... and so on...
    }
}

///////////////////////////////////////////
// method 2

// stuff that can go wrong;

if (test1 == failed)
{
    // print error;
    // exit;
}

// more stuff that can go wrong;

if (test2 == failed)
{
    // print error;
    // exit;
}

// ... and so on...

【问题讨论】:

  • 我建议使用第二种样式,因为它不会影响您的意图。但我想这只是tase。

标签: c error-handling coding-style


【解决方案1】:

有些人不同意我的观点,但我使用 goto 的。在每个函数内部,最后我有一个块,看起来像这样

if (0)
{
ERROR:
  // Handle errors, and exit/return after potentially freeing resources
}

然后我使用if (something_bad) goto ERROR; 而不使用其他或其他东西。

很多人不喜欢 goto,但这是这样做的方法,而不是重复代码。如果你真的坚持不使用 goto,我会这样做:

#define LOCAL_ASSERT(COND) if (COND) { \
  /* Handle errors, and exit/return after potentially freeing resources */ \
}

在每个函数的开头为它添加一个定义,然后在函数的末尾添加一个#undef LOCAL_ASSERT。这允许对每个函数进行不同的错误处理,而不会用不同的宏名称污染整个程序。

然后我只是在任何地方使用LOCAL_ASSERT(cond)

编辑:为了让自己更清楚,这是为了节省多次编写错误处理代码。如果您想要小的自定义,很容易设置错误变量字符串(或将其添加为宏参数)。我只是不喜欢 if else 的东西。我经常这样做

// method 1
if (error) goto ERROR; // no else

// method 2
LOCAL_ASSERT(cond);

否则会污染您的代码并需要更多缩进,这有时很烦人。

【讨论】:

    【解决方案2】:

    我知道这是用户的偏好,但我非常努力地为每个函数建立一个单个退出点,或者最多两个退出点(但它们必须是明显的并且易于断点) :

    const Bool funcFoo(int someval, int someval2, int someval3)
    {
      if(someval == okval)
      { // We're ok
        if(someval2 == okval2)
        { // Still ok.
          if(someval3 == okval3)
          { // Yippee!  We made it!
            return True;  // <===== ONLY SUCCESS RETURN POINT
          }
        }
      }
      // Houston, we had a problem.
      return False;  // <===== ONLY FAIL RETURN POINT
    }
    

    在“else”很重要的情况下,它是类似的展开,但我们只保留两个返回点:

    const Bool funcFoo(int someval, int someval2, int someval3)
    {
      if(someval == okval)
      { // We're ok
        if(someval2 == okval2)
        { // Still ok.
          if(someval3 == okval3)
          { // Yippee!  We made it!
            return True;  // <===== ONLY SUCCESS RETURN POINT
          }
          else
          { // someval3 is bad.
            //...maybe handle, not return.
          }
        }
        else
        { // someval2 is bad.
          // ...maybe handle, not return.
        }
      }
      else
      { // someval is bad.
        // ...maybe handle, not return.
      }
      // Houston, we had a problem.
      return False;  // <===== ONLY FAIL RETURN POINT
    }
    

    有几点要提:

    1. 最好有一个返回点。二 如果返回点是可以接受的 很明显(您的错误通常要求退货或无法继续)。
    2. 有时“else”用于 仅用于调试目的,此时 点我把它们包装在#ifdef _DEBUG ... #endif
    3. 有时“测试”应该用于 成功,有时失败, 取决于哪个是最 适合嵌套。
    4. 这种方法(嵌套测试)是 有时与顺序相关 测试。但是,嵌套测试是 一般首选。

    【讨论】:

      【解决方案3】:

      我投票支持方法 2。正如您所提到的,方法 1 中的错误处理掩盖了“真实”算法的逻辑。

      【讨论】:

        【解决方案4】:

        我宁愿使用这样的东西:

        if (test1 == failed)
        {
            // print error;
            // exit;
        }
        else if (test2 == failed)
        {
            // print error;
            // exit;
        }
        else
        {
            // ... and so on...
        }
        

        它更具可读性并且限制缩进。它还清楚地向我表明,如果一个条件失败,它将尝试所有其他条件,直到最终失败;不可能同时满足两个条件。

        【讨论】:

        • 不过有more stuff that can go wrong,所以你不能以一种好的方式真正做到这一点。
        • 总会有更多可能出错的东西:)。
        猜你喜欢
        • 1970-01-01
        • 2013-03-11
        • 2012-04-20
        • 2018-02-16
        • 2011-07-28
        • 2011-04-21
        • 2021-08-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多