关于现有代码的样式问题(C / C ++)

时间:2009-05-25 20:07:32

标签: c++ c coding-style

我只是希望以下看起来不像你多余的jabber :) 无论如何,有:

for (p = fmt; *p; p++) {
    if (*p != '%') {
        putchar(*p);
        continue;
    }
    switch (*++p) {
        /* Some cases here */
        ...
    }
 }

我想知道为什么作家(Kernighan / Ritchie)在continue声明中使用了if。 我认为仅仅因为他认为它比在switch陈述下缩进整个else更优雅,你觉得怎么样?

11 个答案:

答案 0 :(得分:15)

可能。人类大脑的堆栈空间有限,因此难以处理深层嵌套结构。任何使我们希望解析的信息变得扁平化的东西都会使它更容易理解。

同样,我通常更喜欢这个:

bool foo(int arg)
{
    if(!arg) {
        /* arg can't be 0 */
        return false; 
    }

    /* Do some work */
    return true;
 }

对此:

 bool foo(int arg) 
 { 
     if(!arg) {
         /* arg can't be 0 */ 
         return false; 
     } else {
         /* Do some work */ 
         return true;
     } 
 }

或者更糟糕的是,对此:

bool foo(int arg) 
{ 
    if(arg) {
        /* Do some work */ 
        return true;
    } else {
        /* arg can't be 0 */ 
        return false; 
    } 
}

在最后一个例子中,完成工作的部分可能会很长。当读者达到else条款时,他可能不记得他是如何到达那里的。

将保释条件置于接近开头的条件有助于确保尝试调用您的函数的人能够很好地了解函数所期望的输入。

另外,正如其他人所指出的那样,继续清楚地说明没有必要进一步阅读循环内的代码来确定在这种情况下是否还有更多的处理完成,使代码更容易理解。同样,你强迫读者跟踪的事情越少越好。

答案 1 :(得分:9)

因为继续,很明显代码是为这个循环迭代完成的。如果使用了else,你还要检查else之后是否没有代码。

我认为尽快退出上下文是一般习惯,因为这会导致更清晰的代码。


例如:

if(arg1 == NULL)
  return;

if(arg2 == NULL)
  return;

//Do some stuff

VS

if(arg1 != null)
{
  if(arg2 != null)
  {
    //Do some stuff
  }
}

答案 2 :(得分:2)

当它像这样放置时,阅读起来会容易得多。

我们是否通过循环完成了这次迭代?是吗?因此,让我们继续进行下一次迭代。

答案 3 :(得分:2)

我认为他有足够的理由来缩进交换机下的代码,并且缩小函数的整个部分是非常浪费横向空间。在编写代码时,我想80个字符的宽度仍然很受欢迎。

我认为这并不难理解,但我认为很高兴提到你不要立即做什么,然后是GTFO。

答案 4 :(得分:1)

总有很多方法可以编写这样的代码 -

将整个开关放在else语句中是完全有效的。我想他们这样做的原因〜可能只是他们当时的想法:

“如果p处的值不等于'%',则放置然后继续。”

如果您在其他地方进行了切换,那么在特定情况下,您可能不会明显地跳到下一次迭代。

但这完全是个人风格的选择。我不会太担心 - 只要以对你和你的团队最有意义的方式写它。

答案 5 :(得分:1)

我同意。 但你不能把它视为一个“单纯的理由”,它实际上是一个很好的理由,因为它减少了代码的所有复杂性。使它更短,更容易阅读和理解。

答案 6 :(得分:1)

如果您使用else,那么else内的所有内容都需要缩进:

if ()
{
  doA();
}
else
{
  doB();
  if ()
  {
    doC();
  }
  else
  {
    doD()
  }
}

如果您使用continue,则无需缩进:

if ()
{
  doA()
  continue;
}
doB();
if ()
{
  doC();
  continue;
}
doD();

此外,continue表示我可以停止考虑这种情况:例如,如果我看到else,那么可能会在以后的'%'案例中处理更多循环,即在else语句的末尾;而在看到continue时,我立即知道循环中'%'个案的处理已完全结束。

答案 7 :(得分:1)

最可能的原因是后面的switch相当长 - 这看起来像printf格式解析。

答案 8 :(得分:1)

继续/打破循环可能有更多原因。所以接下来会看:

loop
{
   if (cond1)
   {
      if (cond2)
      {
         if (cond2)
         {
            more conditions...
         }
      }
   }
   else
   {
      the loop action 
   }
}

恕我直言,它不像你的例子中的循环那样优雅和可读,例如:

loop
{
   if (cond1)
      continue;
   if (cond2)
      continue;
   if (cond2)
      continue;   
   if( more conditions...)
      continue;

  the loop action 
}

你甚至不需要理解所有“if”的所有结构(可能要复杂得多)来理解循环逻辑。

P.S。只是为了这个案例:我不认为作者想过如何编写这个循环,他们只是写了:)

答案 9 :(得分:0)

我坚持Dijkstra的教导: goto是有害的。继续/休息是goto的小兄弟。

如果问题是你要将代码缩进太多,那么解决方案就不是在循环中继续,而是通过分离不同函数中的代码或考虑更好的组织方式来降低复杂性。 / p>

例如,@ Kamarey片段会更加清晰:

loop
{
   if (!(cond1 ||
         cond2 ||
         cond2 || 
         ...))
   {
      the loop actions;
   }
}

或@Ori Pessach示例可以表示为:

bool foo(int arg)
{
    if(arg) {
        /*do some work*/
    }

    return arg != 0;
}

简而言之,通常我找不到足够的理由来使用非结构化编程结构(可能在非常有限的情况下使用某些清理代码......)

答案 10 :(得分:-2)

好吧,我写了大约11年的C程序,我不得不读了5遍你的代码来理解它!

Kernighan和Ritchie在六十年代活跃起来。那时,能够理解一段代码是不相关的。能够编写适合16 Ko的代码是。

所以我并不感到惊讶。当你的患者是K& K时,C是一种糟糕的语言。 R.看看realloc:今天谁会知道类似的代码?在60年代,它风靡一时,但现在令人震惊,至少:o)