模式,以防止不断检查错误?

在C中,是否有一种模式可以不再检查调用其他函数的函数中的错误?

例如,如果函数foo()依次调用a(),b()和c(),则必须在继续之前检查每个函数的返回值。 如果a(),b()或c()也调用其他函数,这些函数也可能调用其他函数,这会留下一长串错误检查,可能出现相同的错误……

int16_t a(x_t x) {return DO_SOME_WORK(x);} int16_t b(y_t y) {return DO_OTHER_WORK(y);} int16_t c(z_t z) {return DO_MORE_WORk(z);} foo (x_t x, y_t y, z_t z) { int16_t err = 0; // I can handle errors returned by either: err = a(x)); if (err) return err; err = b(y); if (err) return err; err = c(z); return err; // Or err = a(x); if (!err) err = b(y); if (!err) err = c(z); return err; } 

我更喜欢第二种方法,因为有一个明确的单一退出点,但另一种方法本地化处理和任何一种方法引入了大量的ifs,并且使用大型程序,这是很多额外的代码,特别是在除了调用其他函数之外什么都不做的函数中例如,函数并传递它们之间的输出。 谢谢

在这种情况下,您可以利用&&的短路评估,其中当左侧操作数为false ,不评估右侧操作数。

如果您可能希望在对a()b()c()调用之间执行其他处理,则以下内容允许:

 bool foo (x_t x, y_t y, z_t z) { bool err = a( x ); err = err && b( y ) ; err = err && c( z ) ; return err; } 

而如果函数只由调用a()b()c() ,那么你可以使用更简洁:

 bool foo (x_t x, y_t y, z_t z) { return a( x ) && b( y ) && c( z ) ; } 

[补充回应评论]

解决@kkrambo关于函数返回的任何数值没有传播给调用者的观点可以解决,但作为解决方案的吸引力越来越小:

 int16_t foo (x_t x, y_t y, z_t z) { int16_t ret ; bool err = (ret = a( x )); err = err && (ret = b( y )) ; err = err && (ret = c( z )) ; return ret ; } 

要么

 int16_t foo (x_t x, y_t y, z_t z) { uint16_t ret ; (ret = a( x )) && (ret = b( y )) && (ret = c( z )) ; return ret ; } 

你甚至可以做到可怕的增加:

 int16_t foo (x_t x, y_t y, z_t z) { int16_t err ; if( (err = a(x)) ) {} else if( (err = b(y)) ) {} else if( (err = c(z)) ) {} return err ; } 

简化以下内容,其中err是布尔值:

 bool foo (x_t x, y_t y, z_t z) { bool err = true ; if( a(x) ) {} else if( b(y) ) {} else if( c(z) ) {} else { err = false } return err ; } 

如果函数的每个函数调用都需要错误检查,那么每次调用该函数时都应检查错误。 原因是即使它是相同的函数,输入也是不同的,并且全局(和静态局部)变量是不同的,从而改变结果。 这意味着如果你想检查错误,你实际上并没有浪费计算工作。

编辑:我不知道这是否明智但你可以使用宏来清理语法

 err = a(x); if(err) return err; 

它看起来像

 #define error_check(f, e) \ e = f; \ if(e) return e; 

所以在你的代码中你可以写

 error_check(a(x),err) 

我没有使用宏,但这应该工作。

由于目标是返回错误代码而不是简单的布尔值,因此OP提供的2种方法至少是合理的,如果不是优选的话。 像if (a(x) || b(x)) return true短路代码无法保持从a()b()派生的err值。

它归结为风格。

如果代码简单由几行组成,任何方法都足够了。 但是考虑到a(x)b(x)c(x)之间可能存在重要的代码,建议不要使用样式2,因为它在许多代码行之间分离流。 我喜欢风格3超过1,因为它有点简洁,但1和3之间的差异很小。

为了应对样式#2在一个位置收集错误返回的能力,考虑一个更高级别的函数包装器,它可以明确地确保所有序言和结尾活动的发生。

最佳选择:遵循团队风格指南,否则最适合您维护 ,而不是写作。

 int16_t foo (x_t x, y_t y, z_t z) { // added matching return type int16_t err = 0; // OP style 1 err = a(x)); if (err) return err; err = b(y); if (err) return err; err = c(z); return err; // OP style 2 err = a(x); if (!err) err = b(y); if (!err) err = c(z); return err; // style 3 // via enum or define, create an OK symbol if ((err = a(x)) != OK) return err; if ((err = b(x)) != OK) return err; if ((err = c(x)) != OK) return err; // }