C语言中如何从返回值函数返回错误

C语言中如何从返回值函数返回错误,c,embedded,C,Embedded,我想知道在C中处理返回值函数错误的最佳实践是什么。 首先,我想介绍一下需要,然后分享一些我尝试过的解决方案,并听取不同的想法 问题是,当我有一个返回值函数时,这个函数可以返回范围内的任何值,而这个函数有时会出现一个问题,即它必须返回到调用函数,因此它不能使用传统的返回。 如何处理调用函数中的错误 几点注意: 1.我是一名嵌入式编程人员,我热衷于保持我的函数可重入(纯)函数,使不同的中断不会损害全局函数,我几乎不在代码中使用全局函数 我无法使用0或-1处理它,因为它也是有效的返回 errno解决方

我想知道在C中处理返回值函数错误的最佳实践是什么。 首先,我想介绍一下需要,然后分享一些我尝试过的解决方案,并听取不同的想法

问题是,当我有一个返回值函数时,这个函数可以返回范围内的任何值,而这个函数有时会出现一个问题,即它必须返回到调用函数,因此它不能使用传统的返回。 如何处理调用函数中的错误

几点注意: 1.我是一名嵌入式编程人员,我热衷于保持我的函数可重入(纯)函数,使不同的中断不会损害全局函数,我几乎不在代码中使用全局函数

  • 我无法使用0或-1处理它,因为它也是有效的返回

  • errno解决方案不支持纯函数和1

  • 4.我尝试使用返回结构,其中一个字段用于值,一个字段用于错误(如果发生)

    unsigned int squre(unsigned int num)
    {
    return num*num;    
    }
    
    程序员说,我想有溢出处理

    struct returnUnsignedint squre(unsigned int num)
    {
        struct returnUnsignedint returnValue;
        if (num>65535) { //my embedded system over flow
            returnValue.error = 1;
        }
        returnValue.value = num*num;
    return returnValue;
    }
    
    有更好的选择吗

    如果您有不同的观点或解决方案,请告诉我


    我将感谢任何帮助,谢谢

    另一个选项是返回错误代码并将输出值写入参数:

    int sqr( unsigned int num, unsigned int *result )
    {
      if ( num > 65535 )
        return 0;
    
      *result = num * num;
      return 1;
    }
    
    unsigned int sqr( unsigned int num, int *err ) { ... }
    
    这并不总是最方便的选项(特别是如果您想将
    sqr
    用作更大的算术表达式的一部分,那么这应该满足您的要求

    编辑

    当然,您可以选择另一种方式—返回值并将错误代码写入参数:

    int sqr( unsigned int num, unsigned int *result )
    {
      if ( num > 65535 )
        return 0;
    
      *result = num * num;
      return 1;
    }
    
    unsigned int sqr( unsigned int num, int *err ) { ... }
    
    但坦率地说,我更喜欢第一个版本,因为除非您知道操作成功,否则不会尝试使用返回值

    没有“一刀切”的解决方案,因为它取决于程序的需要

    然而,也有一些可能性

    一种方法是指定函数的一个可能返回值可以指示发生了错误。例如,由于没有签名的
    的值不是每个值都是另一个值的平方,因此选择一个合适的值并返回该值

     unsigned sqre(unsigned x)
     {
         if (x == 0U)
         {
            return 0U;
         }
         else if (UINT_MAX/x >= x)      /*  use fact that UINT_MAX is typically not a perfect square */
         {
            return x*x;
         }
         else
         {
            return UINT_MAX;
         }
     }
    
    (注意,在上面,我已经消除了您的隐式假设,
    无符号
    至少为32位,方法是避免使用神奇值
    65535

    另一个选项是执行一些标准库函数所做的操作:返回
    0
    (或者,在
    无符号的情况下,返回
    0U
    ,如果出现错误),即使该值是有效的也是可行的。这意味着您的函数总是返回一个可用的值,但是如果您的函数返回零,调用方需要决定该怎么做


    另一个选项是返回数据结构

     struct Return
     {
           unsigned value;
           int error;
     };
    
     struct Return sqre(unsigned x)
     {
         struct Return retval;
         retval.error = 0;
         if (x == 0)
         {
            retval.value = 0U;
         }
         else if (UINT_MAX/x >= x)      /*  use fact that UINT_MAX is typically not a perfect square */
         {
            retval.value = x*x;
         }
         else
         {
            retval.error = 1;
         }
         return retval;
     }
    
    取舍是强制调用方创建
    struct
    的实例,然后从中检查或提取数据

    另一个是提供第二个参数,该参数提供错误指示

     unsigned sqre(unsigned x, int *error)
     {
         *error = 0;
         if (x == 0U)
         {
            return 0U;
         }
         else if (UINT_MAX/x >= x)      /*  use fact that UINT_MAX is typically not a perfect square */
         {
            return x*x;
         }
         else
         {
            *error = 1;
            return 0U;       /* falling off end without a return statement gives undefined behaviour */
         }
     }
    
    上述方法的缺点是调用方可能忘记检查错误条件。修改上述内容很简单,因此它会检查
    error
    是否为
    NULL
    ,然后不修改
    *error
    (然后允许调用方指定
    NULL
    ,以表示对错误条件不感兴趣)

    另一种方法是函数返回错误条件,并要求调用方传递变量的地址以保存结果(如果没有发生错误)。这样做的一个缺点是,函数的结果不能直接用于更大的表达式中

    由于从技术上讲,
    无符号的溢出提供了定义良好的行为(基本上是模运算),所以请使用不进行检查的版本。如果函数返回一个
    带符号的int
    (因为溢出会产生未定义的行为),则此选项不可行。这要求调用者处理返回值可能被截断的事实(例如,值的高阶部分丢失)

    另一种选择是,如果发生溢出,则函数将终止,并带有偏见。比如,

     unsigned sqre(unsigned x)
     {
         assert(x == 0 || UINT_MAX/x < x);   /*  from <assert.h> */
         return x*x;
     }
    
    unsigned sqre(unsigned x)
    {
    断言(x==0 | | UINT_MAX/x
    这免除了调用方检查的责任。但是,调用方(如果程序终止是不需要的)必须确保传递的参数有效。或者,最终用户需要愿意接受程序可能会因错误数据而终止。

    接下来,我建议增加一个宏,以便能够在“更大的算术表达式”中使用该函数

    输出为:

    Test case 1:
    sqr(42) = 1764
    Test case 2:
    sqr() failed: Numerical result out of range
    Test case 3:
    sqr() failed: Invalid argument
    Test case 4:
    sqr(1) + sqr(2) = 5
    Test case 5:
    sqr() failed: Numerical result out of range
    
    当测试用例5结束程序时,永远不会达到测试用例6。

    没有“最佳实践”。存在各种方法。什么是最好的取决于更大的编码方案。相反,不要只看错误是如何发出信号的,还要看调用代码如何处理错误。