我想知道处理 C 中返回值函数错误的最佳实践是什么。首先,我想介绍一下需求,然后分享一些我尝试过的解决方案并听取不同的想法。
问题是当我有一个返回值函数时,该函数可以返回范围内的任何值,并且该函数有时会出现一个问题,即它也必须返回到调用函数,因此它不能使用传统的返回值。我该如何处理调用函数中的错误?
几点说明: 1.我是一名嵌入式程序员,我热衷于以不同的中断不会损害全局变量的方式保持我的函数可重入(纯)函数,我很少在代码中使用全局变量。
我无法用 0 或 -1 来处理它,因为它也是有效的返回值。
errno 解决方案不支持纯函数以及 1。
4.我尝试使用返回结构,其中一个字段用于值,一个字段用于错误(如果发生)。
unsigned int squre(unsigned int num)
{
return num*num;
}
Run Code Online (Sandbox Code Playgroud)
程序员说我想要溢出句柄。
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;
}
Run Code Online (Sandbox Code Playgroud)
还有更好的选择吗?
如果您有不同的观点或解决方案,请告诉我。
我将不胜感激任何帮助,谢谢!
不存在“一刀切”的解决方案,因为这取决于您的程序的需求。
然而,有几种可能性。
一种方法是指定函数的一个可能的返回值可以指示发生了错误。例如,由于并非每个值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;
}
}
Run Code Online (Sandbox Code Playgroud)
(请注意,在上面,我unsigned通过避免使用 magic value 消除了至少 32 位的隐含假设65535)。
另一种选择是执行某些标准库函数的操作:返回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;
}
Run Code Online (Sandbox Code Playgroud)
权衡是迫使调用者创建一个实例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 */
}
}
Run Code Online (Sandbox Code Playgroud)
上述的缺点是调用者可能会忘记检查错误情况。修改上面的内容很简单,因此它检查是否error是NULL,然后不修改*error (然后允许调用者指定 aNULL来指示对错误条件不感兴趣)。
另一种方法是让函数返回错误条件,并要求调用者传递变量的地址来保存结果(如果没有发生错误)。这样做的缺点是函数的结果不能直接在较大的表达式中使用。
从技术上讲,由于 的溢出unsigned给出了明确定义的行为(本质上是模算术),因此请使用不进行检查的版本。signed int如果函数返回 a (因为溢出会产生未定义的行为),则此选项不可行。这要求调用者处理返回值可能被截断的事实(例如,值的高位部分丢失)。
还有一种选择是,如果发生溢出,函数就会有偏见地终止。例如;
unsigned sqre(unsigned x)
{
assert(x == 0 || UINT_MAX/x < x); /* from <assert.h> */
return x*x;
}
Run Code Online (Sandbox Code Playgroud)
这消除了调用者进行检查的责任。但是,调用者(如果不希望程序终止)必须确保传递的参数有效。或者,最终用户需要愿意接受程序可能因错误数据而终止。
| 归档时间: |
|
| 查看次数: |
1173 次 |
| 最近记录: |