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

时间:2019-05-18 12:38:30

标签: c embedded

我想知道有一会儿最佳实践来处理C语言中返回值函数的错误。 首先,我想介绍需求,然后分享一些我尝试过的解决方案,并听听不同的想法。

问题是当我有一个返回值函数时,该函数可以返回范围内的任何值,并且该函数有时存在一个问题,即它必须也返回给调用函数,因此不能使用传统的返回值原因。 如何在调用函数中处理该错误?

一些注意事项: 1.我是一名嵌入式程序员,我热衷于保持函数重入(纯)函数的方式,以使不同的中断不会损害全局变量,因此我几乎不会在代码中使用全局变量。

  1. 我不能用0或-1处理它,因为它也是有效的返回值。

  2. errno解决方案不支持纯函数,还不支持1。

4。我尝试使用return结构,如果有错误,我将有一个字段作为值,而有一个字段则为错误。

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;
}

还有更好的选择吗?

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

我将不胜感激,谢谢!

3 个答案:

答案 0 :(得分:4)

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

"terminal.integrated.shellArgs.windows": ["-ExecutionPolicy", "Bypass"]

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

编辑

当然,您总是可以采用其他方法-返回值并将错误代码写入参数:

sqr

但是坦率地说,我更喜欢第一个版本,因为除非您知道操作成功,否则您不会尝试使用返回值。

答案 1 :(得分:4)

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

但是,有几种可能性。

一种方法是指定函数的一个可能的返回值可以指示发生了错误。例如,由于并非unsigned的每个值都是另一个的平方,请选择一个合适的值并将其返回。

 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;
     }
 }

(请注意,在上文中,通过避免使用魔术值unsigned,我消除了您对65535至少为32位的隐含假设)。

另一种选择是执行某些标准库函数的操作:即使可行,也返回0(或者在发生错误时返回unsigned(如果出错,则返回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来指示否对错误情况的关注)。

该函数的另一种选择是返回错误条件,并要求调用者传递变量的地址以保存结果(如果未发生错误)。这样做的一个缺点是该函数的结果不能直接在较大的表达式中使用。

从技术上讲,unsigned的溢出给出了明确定义的行为(本质上是模运算),请使用不进行检查的版本。如果函数返回signed int,则此选项不可行(因为溢出会产生未定义的行为)。这要求调用者处理以下事实:返回的值可能会被截断(例如,丢失的值的高阶部分)。

然而,另一个选择是如果发生溢出,该函数将以有偏见的方式终止。例如;

 unsigned sqre(unsigned x)
 {
     assert(x == 0 || UINT_MAX/x < x);   /*  from <assert.h> */
     return x*x;
 }

这消除了呼叫者检查的责任。但是,调用者(如果不希望终止程序)则必须确保传递的参数有效。另外,最终用户将需要愿意接受程序可能会因错误数据而终止。

答案 2 :(得分:0)

紧跟Johnanswer之后,我提出了一个额外的宏,以便能够在“ 更大的算术表达式”中使用该函数

#include <stdlib.h> /* for EXIT_xxx macros */
#include <stdio.h>  /* for perror() */
#include <errno.h>  /* for errno */


int sqr(unsigned int num, unsigned int *psqr)
{
  int result = 0;

  if (NULL == psqr)
  {
    result = -1;
    errno = EINVAL;
  }
  else if (num > 0xffffU)
  {
    result = -1;
    errno = ERANGE;
  }
  else
  {
    *psqr = num * num;
  }

  return result;
}

#define SQR(x, y) \
  ((-1 == sqr(x, &y)) \
    ? (perror("sqr() failed"), exit(EXIT_FAILURE), 0U) \
    : y \
  )

下面进行一些测试(请注意,如果sqr()失败,则宏SQR()必须终止程序):

int main(void)
{
  unsigned int r, i;

  puts("Test case 1:");
  i = 42;
  if (-1 == sqr(i, &r))
  {
    perror("sqr() failed");
  }
  else
  {
    printf("sqr(%u) = %u\n", i, r);
  }   

  puts("Test case 2:");
  i = 0x10000;
  if (-1 == sqr(i, &r))
  {
    perror("sqr() failed");
  }
  else
  {
    printf("sqr(%u) = %u\n", i, r);
  }

  puts("Test case 3:");
  if (-1 == sqr(i, NULL))
  {
    perror("sqr() failed");
  }
  else
  {
    printf("sqr(%u) = %u\n", i, r);
  }

  puts("Test case 4:");
  r = SQR(1, r) + SQR(2, r);
  printf("sqr(%u) + sqr(%u) = %u\n", 1, 2, r);

  puts("Test case 5:");
  r = SQR(0x10000, r) + SQR(2, r);
  printf("sqr(%u) + sqr(%u) = %u\n", 0x10000, 2, r); 

  puts("Test case 6:");
  r = SQR(NULL, r) + SQR(2, r);
  printf("sqr(%u) + sqr(%u) = %u\n", 0x10000, 2, r); 

  return EXIT_SUCCESS;
}

输出为:

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个测试用例。