如何处理Ansi C中传递的无效参数

如何处理Ansi C中传递的无效参数,c,ansi-c,C,Ansi C,我是C新手,来自较新语言(如Java和C++)的背景,我不知道如何处理运行时错误,例如发送给函数的参数不正确 例如,假设我想编写一个函数来操作字符串(假设任何有效的int都是可接受的返回值): intfoo(chars[]{ 如果(strlen小于1) //错误 .... .... 返回someInt; } 如果我希望函数立即停止,如何在Ansi C中处理这种情况?在C++或java中,我会抛出一个异常,以被调用方捕获。 如果我想让函数立即停止 两件事 如果只希望函数停止执行并返回给调用者,请

我是C新手,来自较新语言(如Java和C++)的背景,我不知道如何处理运行时错误,例如发送给函数的参数不正确

例如,假设我想编写一个函数来操作字符串(假设任何有效的int都是可接受的返回值):

intfoo(chars[]{
如果(strlen小于1)
//错误
....
....
返回someInt;
}
如果我希望函数立即停止,如何在Ansi C中处理这种情况?在C++或java中,我会抛出一个异常,以被调用方捕获。 如果我想让函数立即停止

两件事

  • 如果只希望函数停止执行并返回给调用者,请使用
    return
    语句。此外,您可能希望使用一些预定义或用户定义的错误代码作为故障案例返回值,以区分故障原因和调用方

  • 如果希望程序本身终止,请调用


  • 您始终可以使用返回值:

    if( /* some bad parameters */ )
       return -1;
    
    然后:

    int value = foo( something );
    if( value == -1 )
       // error
    else
       // no error
    
    或传递另一个参数:

    int foo( char s[], int* value )
    {
        if( /* error */ )
            return 1;// error code 1
        // ...
        *value = something;
        return 0;
    }
    
    然后,在调用函数时,可以验证该函数是否正确执行:

     if( foo( "something", &result ) )
     {
         //ok
     }
     else
     {
         // not ok
     }
    

    这两种方法都意味着调用方将手动验证是否存在错误

    如果我正确地考虑了您的问题,大多数类型错误将在编译时被捕获。如果您想早点返回,只需调用
    return
    。通常,在错误处理中,C编程人员会为错误添加一个
    goto

    struct Data *
    create_data ()
    {
      struct Data *data = malloc(sizeof(struct Data));
    
      if (data == NULL)
          goto exit;
    
      /* do stuff with data */
    
    exit:
        return data;
    }
    
    如果没有内存,它将返回NULL。但你可能会因为另一个原因想早点退出。因此,如果您需要优雅地退出该程序(并释放所有已获取的内容),则需要一个中心退出点

    我通常有一个函数,比如

    void bail (const char *fmt, ...)
    {
        va_list argp;
        va_start(argp, fmt);
          vfprintf(stderr, fmt, argp);
        va_end(argp);
        exit(EXIT_FAILURE);
    }
    

    它打印到STDERR,并返回一个非0出口。您可以很容易地修改它以接受任何需要释放的数据。

    在任何C程序中进行错误处理的标准方法是为错误代码保留函数的返回值

    最典型的是,这是一种自定义枚举类型。例如:

    typedef enum
    {
      FOO_OK,
      FOO_ERR_STRLENGTH,
      FOO_ERR_DIVIDE_BY_ZERO,
      ...
    } foo_err_t;
    
    
    foo_err_t  foo_func (/* parameters */)
    {
      if (strlen(s) < 1) 
      {
        return FOO_ERR_STRLENGTH;
      }
    
      ...
    
      return FOO_OK;
    }
    
    typedef枚举
    {
    福好,
    福厄尔斯特朗,
    FOO_ERR_除以0,
    ...
    }错误;
    foo_err_t foo_func(/*参数*/)
    {
    如果(strlen小于1)
    {
    返回FOO_ERR_STRLENGTH;
    }
    ...
    返回FOO_OK;
    }
    
    然后正确地记录函数,并说明它可能返回哪些错误代码,以及导致这些错误代码的原因


    发生错误时应该做什么通常与您的例程无关,而是应由调用者决定的事情。特别是,您的例程决不能决定终止整个程序。该决定应由最外层的调用方(调用堆栈的顶部)做出,即从main()开始。您的主要选项有:

    • 让返回值结合错误状态和实际值。例如,如果您的函数只打算返回非负值,那么您可以说负的返回值表示错误
    • 返回错误代码(
      0
      表示成功),并通过参考参数给出函数的输出
    • 返回函数的输出,并通过引用参数给出错误代码

    其他选项包括使用全局(或线程局部)变量作为错误状态(IMHO不是一个好主意),或者返回一个包含返回信息和错误状态的
    struct

    通常显示一条错误消息,然后执行
    退出(错误代码)
    关于如何处理这样的错误,有许多不同的思想流派,它们常常相互冲突。最常见的是静默返回(如果函数返回
    void
    )或返回带有代码或值的错误(通常是
    -1
    NULL
    ),可能会设置
    errno
    。然而,大多数人似乎都同意的一件事是不要使用
    assert
    或类似的功能进行运行时错误检查,主要是因为它只在调试版本上启用。相关:让我们假设我的目标是尝试并恢复。由于函数返回一个int,但任何int都是有效的,因此有没有方法返回,但让调用方知道函数失败。@Dranob您可以使用特定的样式返回值,例如,
    0
    返回值表示成功,负返回值表示函数调用不成功。现在,根据-ve返回值,您可以检查失败的原因。@dranobob FWIW,您可以检查我建议的概念是避免
    errno
    和其他静态变量。它们会使您的代码变得混乱,特别是当多线程时。@mattmcnaberrno通常是线程本地AFAIKMore,返回值本身将是错误代码和通过引用传递的结果。这被标记为C。请不要用其他编程语言给出答案。@Jongware我将返回值更改为使用错误编解码器,但它不支持bool类型。顺便说一句,它不支持引用。我知道,我根据@Jongware的建议更改为int。我确实更改了指针的引用,虽然这是goto的少数有效用法之一,但我不同意goto error是实现这一点的理想方法。无论如何,您都会希望在某个地方指定错误原因,因此最好只返回错误代码,而不进行任何跳跃。我觉得这取决于您是否希望NULL malloc错误向上浮动(让其他系统处理),或者您的系统是否正在创建处理错误的系统。通常,您希望所有错误都向上浮动“浮动”,特别是在设计库、驱动程序等时。
    typedef enum
    {
      FOO_OK,
      FOO_ERR_STRLENGTH,
      FOO_ERR_DIVIDE_BY_ZERO,
      ...
    } foo_err_t;
    
    
    foo_err_t  foo_func (/* parameters */)
    {
      if (strlen(s) < 1) 
      {
        return FOO_ERR_STRLENGTH;
      }
    
      ...
    
      return FOO_OK;
    }