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