【问题标题】:Most Desirable Way To Handle Function Error Messages?处理函数错误消息的最理想方式?
【发布时间】:2012-09-30 23:39:16
【问题描述】:

假设我有一个函数来执行一个小而特定的任务,该任务很有可能失败。处理出错的最佳方法是什么? (假设我知道问题是什么)。

例如,假设我有一个读取两个字节字符串并返回它的函数:

#include <stdio.h>
#include <stdlib.h>

char *bar(void)
{
    char *foo = malloc(3);
    scanf("%2s", foo);
    return foo;
}

int main(void)
{
    char *foo = bar();
    puts(foo);
    free(foo);
    return 0;
}

上面的例子绝对没有任何错误处理。有两种方法可以实现某种错误处理,但我不确定哪种方法更受欢迎或被认为是最佳实践。

方法一(从函数内部向stderr打印错误信息):

#include <stdio.h>
#include <stdlib.h>

char *bar(void)
{
    char *foo;
    if(!(foo = malloc(3)))
    {
        fputs("\nError! Memory allocation failed.", stderr);
        return 0x00;
    }
    scanf("%2s", foo);
    return foo;
}

int main(void)
{
    char *foo;
    if(!(foo = bar())) return 1;
    puts(foo);
    free(foo);
    return 0;
}

方法2(从调用函数打印错误信息到stderr):

#include <stdio.h>
#include <stdlib.h>

char *bar(void)
{
    char *foo;
    if(!(foo = malloc(3))) return 0x00;
    scanf("%2s", foo);
    return foo;
}

int main(void)
{
    char *foo;
    if(!(foo = bar()))
    {
        fputs("\nError! Memory allocation failed.", stderr); 
        return 1;
    }
    puts(foo);
    free(foo);
    return 0;
}

我几乎认为方法二将是最好的方法,因为这样我可以更具体地了解我的错误消息,具体取决于我当时调用该函数的目的。 对于方法二,我担心的是,如果函数有多个潜在故障点,我将无法打印函数中具体出了什么问题。

伪代码:

 IF FAILUREA
     PRINT "FAILUREA OCCURED"
     RETURN
 IF FAILUREB
     PRINT  "FAILUREB OCCURED"
     RETURN

如果我调用的函数是int,这不会有太大问题,因为这样我就可以根据问题返回一个不同的整数值。但是对于char*,我通常会尝试在失败时返回NULL(因此FAILUREAFAILUREB 都会返回NULL);没有办法知道导致函数失败的原因。

所以我的问题是处理错误消息时的最佳做法是什么?

【问题讨论】:

  • if(!(foo=malloc(3))) return 0;
  • 不,在 bar() 函数中,就在“Method2”下 普通泄漏 malloc()。
  • 首先,您的malloc 没有在任何地方分配该内存......所以无论如何它都是泄漏......但是这里的错误处理非常主观。这完全取决于您要在哪里处理错误(或者如果您在 Linux 内核中编程,他们喜欢让它崩溃以获取回溯!)就我个人而言,我更喜欢在错误发生的函数中处理它保持我的main() 干净,但这完全取决于你。
  • @wildplasser 哦,是的,哇哦,我只是在飞行中写的,我现在会更正它,感谢迈克的输入,我很感激。

标签: c function error-handling return-value stderr


【解决方案1】:

允许调用者处理错误报告更好,因为:

  • 如果函数是库的一部分,stderr 可能不可用,需要使用替代报告机制。
  • 调用代码可能有一个可以采取的替代操作,并且可能不会将函数bar()的失败视为实际失败,无需报告。

如果一个函数有多个可能的失败原因,那么可以将一个参数传递给在失败时更新的函数。然后调用函数可以根据实际失败原因选择适当的操作。例如:

enum Status
{
    STATUS_OK,
    STATUS_MEMORY_ALLOCATION_FAILURE,
    STATUS_ACCESS_DENIED
};

enum Status status;
char* foo = bar(&status);
if (!foo)
{
    if (STATUS_MEMORY_ALLOCATION_FAILURE == status)
    {
        /* report failure. */
    }
    else if (STATUS_ACCESS_DENIED == status)
    {
        /* try somewhere else */
    }
}

【讨论】:

  • 太棒了,正是我想要的。也非常简单和重点。谢谢你的回答!
【解决方案2】:

如果您可以对失败采取任何措施,并且如果您打算这样做,那么您就去做。 否则,你可能会实现一个通用的失败函数,在出错的情况下调用它并每天调用它:

void error(const char* format, ...)
{
  va_list vl;
  va_start(vl, format);
  vfprintf(stderr, format, vl);
  va_end(vl);
  exit(-1);
}

您可以选择将其包装在一个宏中,并为其提供行号和文件名:

#define ERROR(fmt, ...) \
  error("file:'%s',line:%d " fmt, __FILE__, __LINE__, __VA_ARGS__)

这将使控制台中的错误非常容易找出,因为错误消息准确地告诉了文件和其中发生错误的行。

典型用法,没什么花哨的:

char *bar(void)
{
  char *foo;
  if ((foo=malloc(3)) == NULL)
    ERROR("malloc() failed!\n");
  if (scanf("%2s", foo) != 1)
    ERROR("scanf() failed!\n");
  return foo;
}

如果您想对错误进行实际操作,可以使用longjmp() 代替exit(-1) 立即返回调用者(= 执行相应setjmp() 的那个),也许关闭所有打开的文件写入,所以缓冲的数据不会丢失。

如果您正在编写一个简单的编译器,例如,这种error() 对于编译器内部的大多数错误和正在编译的源代码中的问题(例如缺少冒号/括号或其他问题)来说已经绰绰有余了否则会使代码无法编译)。

如果您不能或不想这样做,您需要仔细编写代码,进行适当的清理并返回不同的错误代码,以便将可操作的错误传达给调用者。

【讨论】:

    【解决方案3】:

    如果你的函数返回超过 1 个错误情况,你可以这样做

    #include <stdio.h>
    #include <stdlib.h>
    
    int bar(char **foo)
    {
        if(!(malloc(3))) return 1; /* return error case 1*/
        scanf("%2s", *foo);
        if(!(malloc(4))) return 2; /* return error case 2*/
        return 0; /* no error*/
    }
    
    int catcherror(int error)
    {
        switch (error) {
             case 1: 
                 /*do something 1*/
             case 2: 
                 /*do something 1*/
             case 3: 
                 /*do something 1*/
             case 4: 
                 /*do something 1*/
             case 5: 
                 /*do something 1*/
             default: 
                 /*do something 1*/
         }
    }
    
    int main(void)
    {
        char *foo;
        int error
    
        error = bar(&foo);
        catcherror(error);
        puts(foo);
        free(foo);
        return 0;
    }
    

    如果您的项目包含许多返回常见错误情况的函数,catcherror() 函数可能非常有用

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-10-24
      • 2018-09-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-01
      • 1970-01-01
      相关资源
      最近更新 更多