C语言中处理错误的方式有哪些样式?

3

可能重复:
C代码中的错误处理

大家好。我在一些小项目中使用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...

我建议使用第二种风格,因为它不会影响您的缩进。但我猜那只是品味问题。 - Constantinius
4个回答

5

有些人可能不同意我的观点,但我使用goto语句。在每个函数内部,最后都会有一个类似于以下格式的块:

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

然后我使用了if (something_bad) goto ERROR;,没有任何else或其他东西。

许多人不喜欢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);

使用else会污染代码,并且需要更多缩进,有时很麻烦。


1

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

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. 有时候 "test" 应该是为了成功,有时候是为了失败,具体取决于嵌套中哪个更合适。
  4. 这种方法(嵌套测试)有时与顺序测试相关。然而,嵌套测试通常更受欢迎。

0

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


0
我更愿意使用类似这样的东西:
if (test1 == failed)
{
    // print error;
    // exit;
}
else if (test2 == failed)
{
    // print error;
    // exit;
}
else
{
    // ... and so on...
}

这样写更易读,缩进也更有限制。它还清楚地告诉我,如果一个条件失败了,它将尝试所有其他条件,直到最终失败;没有可能同时满足两个条件。


然而,还有“更多可能出错的东西”,所以你不能真正以一种好的方式来做这件事。 - Jeff Mercado
总会有更多的问题出现 :)。 - alex

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