我想知道有一会儿最佳实践来处理C语言中返回值函数的错误。 首先,我想介绍需求,然后分享一些我尝试过的解决方案,并听听不同的想法。
问题是当我有一个返回值函数时,该函数可以返回范围内的任何值,并且该函数有时存在一个问题,即它必须也返回给调用函数,因此不能使用传统的返回值原因。 如何在调用函数中处理该错误?
一些注意事项: 1.我是一名嵌入式程序员,我热衷于保持函数重入(纯)函数的方式,以使不同的中断不会损害全局变量,因此我几乎不会在代码中使用全局变量。
我不能用0或-1处理它,因为它也是有效的返回值。
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;
}
还有更好的选择吗?
如果您有不同的观点或解决方案,请告诉我。
我将不胜感激,谢谢!
答案 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)
紧跟John的answer之后,我提出了一个额外的宏,以便能够在“ 更大的算术表达式”中使用该函数
#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个测试用例。