繁体   English   中英

在检查 C 子例程的返回值时避免冗余?

[英]Avoiding redundancy in checking the return values from C subroutines?

以下代码中有太多冗余:

int init( )
{
    int err;

    err = init_a();
    if(err)
        return -1;

    err = init_b();
    if(err)
        return -1;

    err = init_c();
    if(err)
        return -1;

   // etc.

    return 0;
}

有没有更好的方法来检查err的返回值?

如果你真的有大量的init函数并且不怕function个指针,

#include <stddef.h>

int init_a(void), init_b(void), init_c(void);
int init(void);

static int (*const func[])(void) = {
    &init_a,
    &init_b,
    &init_c,
    // etc.
};

#define ELEMENTS(array) (sizeof(array) / sizeof(array[0]))

int init(void) {
    for (size_t i = 0; i < ELEMENTS(func); ++i) {
        if (func[i]() != 0) {
            return -1;
        }
    }
    return 0;
}

对于另一个 init_x(),您需要做的就是将其插入到 func[] 数组的适当索引处。 交换和删除功能也非常容易,几乎不会带来不便。 你甚至可以return 1 + i; 如果你想知道哪个 function 失败了。

现在您的 init() 已成为由 func[] 数组驱动的数据(与由 init() 中的语句驱动的代码相反)。

仅当您的 init_* 函数具有相同的原型并采用相同的 arguments(如果有)时,这才有效。

使用短路评估

bool init( )
{
    return init_a() || init_b() || init_c();
}

如果您可以接受使用 boolean 值作为成功或失败的最终指示(在我的示例中 failure = true ),那么短路运算符可以使事情变得非常紧凑。

C 语言采用短路,因此在具有多个操作数的逻辑表达式中,代码将仅“按需要”执行以推导出表达式的最终值。 因此,如果表达式A || B || C中的A为真A || B || C A || B || CBC不予评价。 如果 init_N() 函数均未返回失败,则返回值将为成功。

如果您更喜欢 success = true with return A && B && B ,您当然可以否定逻辑(但您的代码表明 success 是false

另外,如果你想允许错误的原因,即错误代码,被报告给调用者,只需传递一个指向错误代码的指针,并让所有子初始化将它设置为有用的东西:

bool init(int* err)
{
    return init_a(err) || init_b(err) || init_c(err);
}

如果必须按顺序调用这些初始化子过程,这在硬件/驱动软件中很常见,则需要在每个步骤后进行状态检查。 在您的示例中,返回不同的错误代码更具指示性。

int init(void) {
  int err = init_a();
  if (0 != err) {
    return -1;
  }

  err = init_b();
  if (0 != err) {
    return -2;
  }

  err = init_c();
  if (0 != err) {
    return -3;
  }

  // etc.

  return 0;
}

以下代码中有太多冗余:

我不知道那件事。 有一个重复的主题,我想这就是你在说的,但没有什么是多余的。 如果您正在寻找 DRYer 代码,那么可以借助预处理器宏来解决此类问题:

#define RETURN_IF_NZ(x, r) do { if (x) return (r); } while (0)

int init(void) {
    RETURN_IF_NZ(init_a(), -1);
    RETURN_IF_NZ(init_b(), -1);
    RETURN_IF_NZ(init_c(), -1);

    // ...

    return 0;
}

优点包括:

  • 没有(显式)重复错误处理代码
  • function 的行为更容易遵循(一旦理解了宏),因为关键元素较少被错误处理脚手架所掩盖

缺点包括:

  • 不像原始版本那么容易调试(但比已经提出的一些替代方案更容易)
  • 人类必须知道、猜测或查找宏的含义

一些人不太重视宏的使用,因为对于大多数用途来说,函数应该是首选。 但是,这是宏可以处理而函数不能处理的情况之一。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM