【问题标题】:Why use #if 0 for block commenting out?为什么使用#if 0 来阻止注释掉?
【发布时间】:2011-04-07 12:47:53
【问题描述】:

逆向工程代码,我对这种风格有点震惊,但我想确保没有充分的理由做这些事情......

是我自己还是这是一种可怕的编码风格

if ( pwbuf ) sprintf(username,"%s",pwbuf->pw_name);
else sprintf(username,"%d",user_id);

以及为什么将不用于编译的代码包装在

#if 0
....
#endif

而不是 cmets?


编辑:正如下面的一些解释,这是由于我没有意识到的混乱 /* */ 的可能性。

但我还是不明白,为什么不直接使用你的编程环境工具或喜欢的文本编辑器的宏来阻止使用“//”注释掉它

这不是更直接,更容易在视觉上跳过吗?


我只是对 C 缺乏经验并且想念为什么这些东西可能是一个好主意 - 还是没有任何借口,我有理由对这段代码的丑陋感到恼火?

【问题讨论】:

  • 我以前做过,出于某种原因,它看起来更清晰。从未见过用于注释代码的预处理器指令。
  • @Novikov:我已经看到很多预处理器指令的使用:D
  • 有点丑,但不是完整的fugly。只需戴上轻度到中度的啤酒护目镜,它就有机会看起来更好。
  • 我已经完成了预处理器指令的事情。优点是它可以维护您的代码格式和颜色编码。打开代码块也比 cmets 更容易。
  • 它还可以最大限度地减少差异并使补丁更易于阅读。

标签: c compilation c-preprocessor


【解决方案1】:

#if 0 在被移除的块包含 block-cmets 时经常使用

我不会说这是一个好习惯,但我经常看到它。

单行流控制+语句很容易理解,尽管我个人避免使用它(而且我工作过的大多数编码指南都禁止它)

顺便说一句,我可能会将标题编辑为有点用处“为什么使用#if 0 而不是块 cmets”

如果你有以下情况

#if 0
        silly();
        if(foo)
           bar();
        /* baz is a flumuxiation */
        baz = fib+3;
#endif

如果你天真地将#if 0/#endif 替换为/* */,这将导致注释在flumuxation 之后立即结束,当你点击*/ 代替#endif 时会导致语法错误以上..

编辑:最后一点,#if 0 语法通常只在开发时使用,特别是如果您必须支持多个版本或依赖项或硬件平台。代码修改为

并不稀奇
#ifdef _COMPILED_WITHOUT_FEATURE_BAZ_
    much_code();
#endif

使用集中式标头定义(或不​​定义)数百个 #define 常量。这不是世界上最漂亮的东西,但每次我在一个体面的项目上工作时,我们都会使用运行时开关、编译时常量(this)、编译时编译决策(只是使用不同的 . cpp 取决于版本),以及偶尔的模板解决方案。这一切都取决于细节。

虽然您是开发人员,但首先要让事情正常工作,但如果您不确定旧代码是否仍然有价值,#if 0 很常见。

【讨论】:

  • 一个不错的编辑器也会使#if 0'd 代码变灰,所以这不是什么大问题。使用示例进行编辑。
  • @Jason 你不能使用 /**/ 来注释掉其他的 /**/,它们不会嵌套。
  • 正如 jkerian 指出的那样,如果您注释掉的代码已经有一个块引用,那么添加另一组块引用将不起作用。使用 #if 0 是一种更好的方法。
  • @Jasaon:因为/* */ cmets 不嵌套。因此,如果您使用它们来隐藏本身包含注释的代码,则(外部)注释将在第一个 */ 处结束。
  • 虽然这在大多数编辑器中是可行的,但我建议您考虑一下“/* */”是“块 cmets”和 // 表示“行注释”这一事实。#if 0机制的工作方式与块 cmets 非常相似,并且它会被首选是有道理的。
【解决方案2】:

那是非常惯用的 C 语言。我不明白它有什么问题。这不是一段漂亮的代码,但它很容易阅读,并且即使没有上下文也很清楚发生了什么以及为什么。

变量名可能会更好,使用snprintfstrncpy 可能更安全。

如果您认为它可以更好,您希望它是什么样的?

我可能会稍作改动:

char username[32];
strncpy(username, 30, (pwbuf ? pwbuf->pw_name : user_id));
username[31] = '\0';

【讨论】:

  • 我想我更愿意看到 else 逻辑放在下一行。我的意思是我了解它在做什么,但是当我第一次看到“else”然后看起来像一个条件时,我的大脑有点融化了。只是出于某种原因让我很烦。再次,也许这不合逻辑,这就是我问这个问题的原因......也许我必须重新训练我的大脑以认为这是一个好主意?
  • 在这种情况下,“轻微更改”根本不起作用,因为user_idint,需要使用%d 进行渲染。
【解决方案3】:

哇!不要反应过度...

我会说它比其他任何东西都更不一致的间距。我有时间发现最好将简短的语句与它们的 IF 放在同一行,尽管这些语句会延长它。

内联样式更适合垂直简洁...可以很容易地分成 4 行,更多行

if (pwbuf) 
  sprintf(username,"%s",pwbuf->pw_name); 
else 
  sprintf(username,"%d",user_id); 

我个人讨厌下一种风格,因为它冗长,难以浏览文件。

if (pwbuf) 
{
  sprintf(username,"%s",pwbuf->pw_name); 
}
else
{ 
  sprintf(username,"%d",user_id); 
}

【讨论】:

  • 首选额外括号的原因是为了避免有人稍后添加额外的行并认为它将成为原始条款的一部分。
  • 方括号更具前瞻性。
  • 如果 goto fail 教会了我们什么...stackoverflow.com/questions/21999473/… 我会将花括号与ifelse(最后一个除外)放在同一行以减少行数。
  • #if 0/#if 1 语句呢?
【解决方案4】:

显然,每个人对这类事情都有自己的看法。所以这是我的:

永远不会写出像上面这样的代码,而且我会少考虑任何这样做的人。我数不清有多少次人们认为没有范围括号就可以逃脱,然后被它咬了。

把控制语句和代码块放在同一行就更糟糕了;缺少缩进使得在阅读时更难看到流控制。一旦你编码了几年,你就会习惯于能够快速准确地阅读和解释代码,只要你能依赖某些视觉提示。为“特殊情况”规避这些提示意味着读者必须停下来重复一遍,没有充分的理由。

另一方面,#if (0) 在开发过程中是可以的,但是一旦代码“稳定”就应该删除(或者至少用一些有意义的预处理器符号名称替换 0)。

【讨论】:

  • 当我主要使用 vi 在 80 列 25 行 VT-100 克隆终端上以 2400 波特拨号到 VAX 上的 BSD 进行编码时,每一行屏幕都很宝贵,所以 if (a) b(); else c(); 是对我来说,这比把它分成四行更容易接受,而且将大括号放在自己的行上的习惯实际上是不可想象的。今天,我更倾向于大方地使用垂直空白。我怀疑,你必须生活在真实终端的限制范围内才能体会到简洁风格的好处。
  • 我仍然会像今天的示例那样编写代码,前提是它完全适合一行。当它折叠成多个时,它需要大括号来避免明显的风险。一方面,在额外的语句中意外编辑的诱惑更少。
  • 我猜有意义的预处理器符号名称是可以的,只要它们明确#undef'd。 #ifdef NOT_THIS 之类的问题在于,必须搜索所有包含的文件以查看 NOT_THIS 是否已定义。如果找到#undef NOT_THIS,您就有了答案。如果你不这样做,你会想知道:“我查看了所有文件吗?”。 #if 0 没有这样的问题。
  • 没有。切勿将 #if 0 中的 0 替换为其他任何内容。因为有人可能会出现并定义宏并无意中包含代码。您实际上应该做的是完全删除 #if 0 块并改用您的版本控制系统。
  • @JeremyP:我认为#if (DEBUG_MODE)#if (DISPLAY_GRAPHS) 之类的东西对于打开或关闭可能仍然有用的东西没有太大问题。但是,对于不再需要的实验代码,我同意,删除它。
【解决方案5】:

上述几点。但是显示器是宽屏的,这些天,我有点不介意

if (pwbuf) sprintf(username,"%s",pwbuf->pw_name);
else       sprintf(username,"%d",user_id);

我的屏幕上似乎总是有太多的水平空间,而没有足够的垂直空间!

另外,如果代码块已经有预处理指令,不要使用#if 0;如果代码已经有block cmets,不要使用/* */。如果两者都有,要么求助于具有 ctrl+/ 的编辑器来注释掉很多行。如果没有,你就完蛋了,直接删除代码!

【讨论】:

  • 来自 C 标准 (WG14 N1124 - ISO/IEC 9899:1999; 6.4.9):“除了在字符常量、字符串文字或注释中,字符 // 引入注释包括直到但不包括下一个换行符的所有多字节字符。"
  • @Bill:那是 C99。 // 不是 C89 或 C90 的一部分,尽管大多数编译器都会接受它。
  • 预处理器块代码停用(它不是真正的注释)确实嵌套。
  • 正如 nmichaels 所说,预处理器 if 块可以嵌套。为了清楚起见,预处理器#if 块应使用/* */ cmets(或// cmets,如果您愿意,并且有一个与C99 兼容的编译器,但这可能会让人们失望)进行记录,所以#if 0 块不应该如果注释得当,则内容的可读性会降低。
【解决方案6】:
if ( pwbuf ) sprintf(username,"%s",pwbuf->pw_name);
else sprintf(username,"%d",user_id);

地道而简洁。如果它被触摸超过 2 或 3 次,我会用括号括起来并下一行。如果添加日志信息或其他条件,它就不是很容易维护。

#if 0
....
#endif

是否可以打开调试代码块。此外,将避免与试图阻止此类评论相关的编译错误:

/* line comment */
...
/* line comment again */

由于 C 块 cmets 不嵌套。

【讨论】:

    【解决方案7】:

    就使用// 进行块评论而言,我能想到的一个原因是,如果您将该代码检查到源代码控制系统中,则责备日志将显示您是这些行的最后一个编辑器代码。虽然您可能希望将评论归因于您,但同时代码本身也归因于您。当然,如果您需要检查代码的“真正”作者的责任日志,您可以返回并查看以前的修订版,但如果在当前修订版中保留该信息会节省时间。

    【讨论】:

    • 好点,但同时,如果您使用源代码控制,您可能不应该注释掉死代码。您可能应该删除它。
    【解决方案8】:

    除了 C 样式 cmets 不嵌套的问题外,如果您使用支持代码折叠的编辑器,使用 #if 0 禁用代码块的优点是可以折叠。在任何编辑器中也很容易做到这一点,而在没有编辑器支持/宏的情况下,使用 C++ 风格的 cmets 禁用大块代码可能会很笨拙。

    此外,许多#if 0 块也有一个else 块。这提供了一种在两个实现/算法之间进行交换的简单方法,并且可以说比批量注释掉一个部分和批量取消注释另一个部分更不容易出错。但是,在这种情况下,您最好使用更易读的东西,例如 #if DEBUG

    【讨论】:

      【解决方案9】:

      评论是cmets。他们描述了代码。

      从编译中排除的代码是代码,而不是 cmets。它通常会包含 cmets,用于描述目前尚未编译的代码。

      它们是两个不同的概念,强制使用相同的语法让我觉得是个错误。


      我正在编辑这个,因为我正在进行大规模的重构,并且我正在大量使用这种模式。

      作为此重构的一部分,我将删除一些广泛使用的类型,并将它们替换为另一种类型。结果当然是什么都不会构建。

      而且我真的很讨厌花几天时间解决一个又一个问题,希望当我完成后,一切都会构建并且所有测试都会运行。

      所以我的第一步是#ifdef-out 所有不能编译的代码,然后[忽略]所有调用它的单元测试。完成此操作后,所有内容均已构建,并且所有未忽略的测试都通过了。

      结果是很多函数看起来像这样:

      public void MyFunction()
      {
      #if true
          throw new NotImplementedException("JT-123");
      #else
          // all the existing code that won't compile
      #endif
      }
      

      然后我取消忽略单元测试,一次一个,然后修复函数,一次一个。

      我需要几天的时间来完成所有这些,所有这些#if 都将消失,在我创建拉取请求以合并它之前,但我发现它在此过程中很有帮助。

      【讨论】:

      • +1 这是一个很好的合理化解释。您为 always 使用预处理器排除代码提出了一个很好的论据。当然它有点丑,但是死代码应该很丑,对吧?
      • 有人可能会指出,注释掉未使用的代码(无论是使用 cmets 还是预处理器指令)是一种滥用,因为这是 SCM 的任务。如果您想保留旧版本的代码(例如,为了避免两次犯错),此代码肯定会成为静态注释(代码与有用的解释混合),不再与代码有任何关系你的程序。鉴于从编译中排除代码总是错误的。
      • 我不会一直这么说,但总的来说,我同意你的看法。我认为正确的做法是在入住前清理这种烂摊子。
      【解决方案10】:

      当它支持代码的对称性并且行不会太长时,我偶尔会使用更简洁的样式。举一个人为的例子:

      if (strcmp(s, "foo") == 0)
      {
          bitmap = 0x00000001UL;
          bit = 0;
      }
      else if (strcmp(s, "bar") == 0)
      {
          bitmap = 0x00000002UL;
          bit = 1;
      }
      else if (strcmp(s, "baz") == 0)
      {
          bitmap = 0x00000003UL;
          bit = 2;
      }
      else if (strcmp(s, "qux") == 0)
      {
          bitmap = 0x00000008UL;
          bit = 3;
      }
      else
      {
          bitmap = 0;
          bit = -1;
      }
      

      还有简洁版:

      if      (strcmp(s, "foo") == 0) { bitmap = 0x00000001UL; bit = 0;  }
      else if (strcmp(s, "bar") == 0) { bitmap = 0x00000002UL; bit = 1;  }
      else if (strcmp(s, "baz") == 0) { bitmap = 0x00000003UL; bit = 2;  }
      else if (strcmp(s, "qux") == 0) { bitmap = 0x00000008UL; bit = 3;  }
      else                            { bitmap = 0;            bit = -1; }
      

      虫子更有可能直接跳到你的脸上。

      免责声明:正如我所说,这个例子是人为的。随意讨论 strcmp、幻数的使用以及基于表格的方法是否会更好。 ;)

      【讨论】:

        【解决方案11】:

        #if 0 ... #endif 在较旧的 C 代码中很常见。原因是用 C 风格的 cmets /* .... */ 注释不起作用,因为 cmets 不嵌套。

        尽管它很常见,但我想说它在现代代码中没有位置。人们在过去这样做是因为他们的文本编辑器无法自动阻止评论大段。更相关的是,他们没有像我们现在那样拥有适当的源代码控制。没有任何借口可以在生产代码中留下注释或#ifdef。

        【讨论】:

          【解决方案12】:

          #if 是一个宏,它检查写在它上面的条件,因为'0'代表一个假,这意味着写在'#if 0'和'#endif'之间的代码块将不会被编译因此可以被视为 cmets。

          所以,我们基本上可以说#if 0 用于在程序中编写cmets。

          例子:

          #if 0
          int a;
          int b;
          int c = a + b;
          #endif
          

          写在“#if 0”和“#endif”之间的部分被认为是cmets。

          问题来了:“/* ... */”可以用于在程序中编写cmets,那么为什么是“#if 0”?

          答案:这是因为,#if 0 可以用于嵌套 cmets,但嵌套 cmets 不被“/* ... */”支持

          什么是嵌套的 cmets?嵌套注释是指 cmets 下的 cmets,可用于各种情况,例如:

          让我们举一个例子,你编写了如下代码:

          现在,有人正在审查您的代码并想在您的程序中评论整段代码,因为他觉得不需要这段代码。一种常见的方法是:

          上面是嵌套cmets的例子。上面代码的问题是,只要遇到“/”之后的第一个“/”,注释就到此结束。 即,在上面的例子中,语句:int d = a-b;没有评论。

          这可以通过使用“if 0”来解决:

          在这里,我们使用了 #if 0 的嵌套 cmets。

          【讨论】:

            【解决方案13】:

            我可以说出使用#if 0 的几个原因:

            • cmets 不嵌套,#if 指令可以;

            • 更方便:如果你想暂时启用一个禁用的代码块,使用#if 0你只需要输入1而不是0。对于/* */,您必须同时删除/**/

            • 你可以放一个有意义的宏来代替0,比如ENABLE_FEATURE_FOO

            • 自动格式化工具将格式化#if块内的代码,但忽略注释掉的代码;

            • grep 查找#if 比查找 cmets 更容易;

            • 它与 VCS 配合得更好,因为您无需接触原始代码,只需在其周围添加行。

            【讨论】:

              猜你喜欢
              • 2016-08-22
              • 2015-11-18
              • 2013-04-28
              • 1970-01-01
              • 2013-01-02
              • 2014-08-05
              • 2013-04-29
              • 1970-01-01
              • 2014-01-08
              相关资源
              最近更新 更多