构建这种代码的最佳方法是什么?

时间:2011-05-05 07:45:41

标签: c loops coding-style design-patterns

我有一个功能,需要分析数据包后的数据包,并决定做什么。 对于每个数据包,代码必须:

  1. 读取数据包,超时时返回错误代码。
  2. 检查是否有损坏,如果是正面记录,请转到1.
  3. 检查中止数据包,如果是正数,则返回并中止代码。
  4. 检查数据包参数的非法性,如果正数记录,则使用无效的参数数据包进行响应并转到1.
  5. 运行数据包的操作,如果记录失败,则使用故障数据包进行响应并转到1.
  6. 如果数据包是结束数据包,则返回成功。
  7. 我的代码如下:

    Packet p;
    for (;;) {  
        int ret = receive(&p, time);
        if (ret == TIMEOUT) {
            log("timeout");
            return TIMEOUT;
        }
        if (ret != 0) {
            log("corrupted %d", ret);
            continue;
        }
    
        if (p.type == ABORT) {
            log("abort");
            return ABORT;
        }
    
        ret = check(&p);
        if (ret != 0) {
            log("invalid %d", ret);
            respond(&p, INVALID);
            continue;
        }
    
        ret = execute(&p);
        if (ret != 0) {
            log("failure %d", ret);
            respond(&p, FAILURE);
            continue;
        }
    
        if (is_last(&p)) {
            finalize(&p);
            return 0;
        }
    }
    

    这段代码是否有更好的结构化方式,不是不必要的嵌套或长期?

6 个答案:

答案 0 :(得分:1)

您可以使用return而不是在循环中使用多个break并进行最终return

Packet p;
int ret;
for (;;) {  
    ret = receive(&p, time);
    if (ret == TIMEOUT) {
        log("timeout");
        break;
    }
    if (ret != 0) {
        log("corrupted %d", ret);
        continue;
    }

    if (p.type == ABORT) {
        log("abort");
        break;
    }

    .
    .
    .

    if (is_last(&p)) {
        finalize(&p);
        ret = 0;
        break;
    }
}
return ret;

答案 1 :(得分:0)

我觉得它看起来不错。它绝对没有不必要的嵌套,即使看起来“很长”也很简短 - 除非你想在receive()check()execute()函数中移动日志。

答案 2 :(得分:0)

我会尽量避免从循环内部返回。 而是在函数结束时中断并返回单个返回值。 除此之外看起来还不错。

答案 3 :(得分:0)

这是个人偏好,但我个人不喜欢无限循环或continue关键字。我会这样做:

Packet p = { /* some dummy init that doesn't flag islast() true */};
int ret = 0;
while (ret != TIMEOUT && p.type != ABORT && !islast(&p)) 
{  
    int ret = receive(&p, time);
    if (ret != TIMEOUT) 
    {
        if (ret != 0) 
        {
            log("corrupted %d", ret);
        }
        else if (p.type != ABORT)
        {
            ret = check(&p);
            if (ret != 0) 
            {
                log("invalid %d", ret);
                respond(&p, INVALID);
            }
            else
            {
                ret = execute(&p);
                if (ret != 0) 
                {
                    log("failure %d", ret);
                    respond(&p, FAILURE);
                }
            }
        }
    }
}

if (ret == TIMEOUT)
{
    log("timeout");
}
else if (p.type == ABORT)
{
    log("abort");
    ret = ABORT;
}
else
{
    finalise(&p);
}
return ret;

我的版本看起来比你的版本复杂,但这是因为它更准确地反映了算法的结构。在我看来(这只是一个意见),像continue和break这样的关键词会混淆结构,不应该使用。

除此之外,另一个主要优点是,在我的版本中,您可以通过查看一个位置(即循环条件)清楚地看到导致循环退出的条件。此外,导致循环的条件完全在循环外处理 - 概念上是正确的位置。该功能也只有一个退出点。

答案 4 :(得分:0)

经验法则是避免一遍又一遍地重复使用同一个变量。如果它用于新的东西,请改为创建一个新的。例如,当我阅读您的代码时,我错过了ret在此过程中重新定义的事实。另一个优点是,如果定义并立即使用某个值,您通常可以在较小的范围内定义它。

例如:

ret = execute(&p);
if (ret != 0) {
    log("failure %d", ret);
    respond(&p, FAILURE);
    continue;
}

您可以将其重写为:

{
  int ret_exe = execute(&p);
  if (ret_exe != 0) {
      log("failure %d", ret_exe);
      respond(&p, FAILURE);
      continue;
  }
}

或者,如果您使用 C99或 C ++:

if (int ret_exe = execute(&p)) {
  log("failure %d", ret_exe);
  respond(&p, FAILURE);
  continue;
}

答案 5 :(得分:0)

很多人不喜欢嵌套在if语句中的赋值,但我认为没有任何合理的依据。使用它们允许紧凑的代码如下(我没有声称是“最好的”;这是非常主观的):

for( ;; ){
    Packet p;
    int ret;

    if( (ret = receive(&p, time)) == TIMEOUT ){
        log("timeout");
        return TIMEOUT;
    }else if( ret != 0 ){
        log("corrupted %d", ret);
    }else if( p.type == ABORT ){
        log("abort");
        return ABORT;
    }else if( (ret = check(&p)) != 0 ){
        log("invalid %d", ret);
        respond(&p, INVALID);
    }else if( (ret = execute(&p)) != 0 ){
        log("failure %d", ret);
        respond(&p, FAILURE);
    }else if( is_last(&p) ){
        finalize(&p);
        return 0;
    }
}