C 处理函数错误消息的最理想方法?

C 处理函数错误消息的最理想方法?,c,function,error-handling,return-value,stderr,C,Function,Error Handling,Return Value,Stderr,假设我有一个函数来执行一个小而特殊的任务,这个任务很有可能失败。处理出问题的最好方法是什么?(假设我知道问题所在) 例如,假设我有一个函数,它读取一个双字节字符串并返回它: #include <stdio.h> #include <stdlib.h> char *bar(void) { char *foo = malloc(3); scanf("%2s", foo); return foo; } int main(void) { cha

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

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

#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;
}
如果我调用的函数是一个
int
,这就不会有太大问题,因为这样我就可以根据出错的地方返回一个不同的整数值。但是在
char*
的情况下,我通常尝试在失败时返回
NULL
(因此
FAILUREA
FAILUREB
都将返回
NULL
);没有办法知道是什么导致函数失败


因此,我的问题是,在处理错误消息时,什么是最佳做法?

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

  • 如果该功能构成库的一部分,则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 */
    }
}

如果函数返回超过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;
}
#包括
#包括
整型条(字符**foo)
{
if(!(malloc(3)))返回1;/*返回错误案例1*/
scanf(“%2s”,*foo);
if(!(malloc(4)))返回2;/*返回错误案例2*/
返回0;/*无错误*/
}
int CATCH错误(int错误)
{
开关(错误){
案例1:
/*做点什么*/
案例2:
/*做点什么*/
案例3:
/*做点什么*/
案例4:
/*做点什么*/
案例5:
/*做点什么*/
违约:
/*做点什么*/
}
}
内部主(空)
{
char*foo;
整数错误
错误=条形图(&foo);
捕捉错误(错误);
看跌期权(foo);
免费(foo);
返回0;
}

如果您的项目包含许多返回常见错误案例的函数,那么
catcherror()
函数可能非常有用如果您可以对故障采取任何措施,并且如果您打算这样做,那么您可以这样做。 否则,您可以实现一个通用故障函数,在发生错误时调用该函数,并在一天内调用该函数:

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()
对于编译器内部的大多数错误和正在编译的源代码中的问题(例如,缺少冒号/paren或其他使代码无法编译的东西)来说已经足够了


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

If(!(foo=malloc(3)))返回0否,在bar()函数中,就在“Method2”malloc()下面。首先,您的
malloc
不会在任何位置分配该内存。。。所以不管发生什么,这都是一个漏洞。。。但这里的错误处理相当主观。处理错误的位置由您决定(或者如果您在Linux内核中编程,他们喜欢让它崩溃以获得回溯!)我个人更喜欢在函数中处理它,在函数中错误会保持我的
main()
干净,但是这真的取决于你。@wildplasser哦,是的,woops只是在飞行中写下了这一点,我现在就纠正它,谢谢你的输入,迈克,我很感激。太棒了,正是我想要的。也很简单,切中要害。谢谢你的回答!
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;
}