【问题标题】:Clean error handling approach in CC 中的干净错误处理方法
【发布时间】:2012-04-20 12:41:57
【问题描述】:

我正在编写一个需要良好错误处理的 C 程序。代码如下:

If(doWork("A")<0){
    return -1;   
}
If(doWork("B")<0){
    undoWork("A");
    return -1;
}
If(doWork("C")<0){
    undoWork("A");
    undoWork("B");
    return -1;
}
return 0;

这段代码有效,但看起来很乱,尤其是我有一长串doWork(X) 可以调用。在这种情况下是否有更好、更简洁的方法来处理错误?

【问题讨论】:

  • 有很多好的答案,但我能提供的最好的答案是与他们一起发表评论:无论你采取什么方法,你总是会尽量减少这样的丑陋,并通过最小化数量来最大化稳健性有失败案例的步骤。例如,预先计算您需要的内存量并在开始之前执行单个malloc,而不是在每个步骤中分配少量内存,可以使许多具有复杂错误输出逻辑的任务变得简单。

标签: c error-handling


【解决方案1】:

有些人,尤其是初学者到中级程序员,对在生产代码中看到goto 有一种非常特殊的反应,但是顺序获取资源及其在错误时智能释放的惯用语如下:

if(doWork("A") < 0)
  goto errA;

if(doWork("B") < 0)
  goto errB;

if(doWork("C") < 0)
  goto errC;

/* success! */
return 0;

/* Error handling / releasing resources section */
errC:
  undoWork("B");
errB:
  undoWork("A");
errA:

return -1;

您会在系统代码中看到大量示例,例如在 linux 内核中。

【讨论】:

    【解决方案2】:

    作为doWork的同一个任务,你大概可以定义一个jobs的链表或者向量,作为参数传递给doWork,在函数内部把对应的信息附加到这个列表中,只调用@ 987654324@一次:

    If(doWork("A", &jobs)<0){
        return -1;   
    }
    If(doWork("B", &jobs)<0){
        undoWork(jobs);
        return -1;
    }
    If(doWork("C", &jobs)<0){
        undoWork(jobs);
        return -1;
    }
    return 0;
    

    这样,无论要撤消的作业组合如何,您的逻辑都不会变得过于复杂。

    与@twain249 的解决方案相比,优势在于该函数决定是否将作业添加到列表中,因此您具有很好的隔离性和模块化。

    您当然可以将某种形式的可交互数据结构与此结合,以进一步减少重复代码的数量

    for(i=0; i < jobdata.size; i++) {
        If(doWork(jobdata[i], &jobs)<0){
            undowork(jobs);
            return -1;   
        }
    }
    

    如您所见,数据结构设计在算法设计中起着重要作用,通常比人们通常认为的重要得多。

    可能有数千个工作,代码将保持四行。

    【讨论】:

      【解决方案3】:

      可能不会。 C++ 和 C# 等较新的语言更喜欢使用异常来帮助改善这种情况。

      也许您可以有一个表格,以某种方式指示您已完成哪些任务并撤消这些任务。但我真的认为这会让你的代码更复杂,而不是更少。

      另外请注意,虽然使用goto 有一些非常强烈的感觉,但实际上有时可以简化这样的结构。

      【讨论】:

      • 即使像 golang 这样的新语言也有更好的结构,比如 defer
      【解决方案4】:

      如果可以将您必须调用 doWork 的所有内容存储在一个数组中,那么您可以显着缩短代码。

      int i = 0;
      int len = MAX_NUM; //set to the value of calls
      int error = 0;
      
      for(i = 0; i < len; i++) {
          if(doWork(a[i]) < 0) {
              error = 1;
              break;
          }
      }
      
      if(error) {
          for(int j = 0; j < i; i++) {
              undoWork(a[j]);
          }
          return -1;
      }
      

      【讨论】:

        【解决方案5】:

        如果你没有超长的列表,你可以这样处理。

        if (dowork("A") >=0) {
        if (dowork("B") >=0) {
        if (dowork("C") >=0) {
        if (dowork("D") >=0) return 0;
        undowork("C"); }
        undowork("B"); }
        undowork("A"); }
        return -1;
        

        【讨论】:

        • 我不明白为什么人们反对这个。编译后,它将与执行 goto/label 技术完全相同。
        【解决方案6】:

        还有另一种广泛使用的方法,它基于清晰且不需要 goto 的单遍循环。这意味着尽管 Undo 函数可以正确处理已完成和未完成的工作。

        do
        {
          if(doWork("A")<0)
            break;   
        
          if(doWork("B")<0)
            break;
        
          if(doWork("C")<0)
            break;
        
          return 0;
        }
        while(0);
        
        undoWork("A");
        undoWork("B");
        undoWork("C");
        return -1;
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-07-14
          • 1970-01-01
          • 1970-01-01
          • 2011-12-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-08-11
          相关资源
          最近更新 更多