【问题标题】:Can you #define a comment in C?你能在 C 中#define 一个注释吗?
【发布时间】:2010-12-19 14:29:13
【问题描述】:

我正在尝试做一个调试系统,但它似乎不起作用。

我想要完成的是这样的:

#ifndef DEBUG
    #define printd //
#else
    #define printd printf
#endif

有没有办法做到这一点?我有很多调试消息,我不想这样做:

if (DEBUG)
    printf(...)

code

if (DEBUG)
    printf(...)

...

【问题讨论】:

  • stackoverflow.com/questions/1644868/… 的直接复制品,可能还有其他人。
  • @JonathanLeffler 用例(创建仅调试打印宏)与stackoverflow.com/questions/1644868/… 重复,但提出的字面问题(我可以在其中包含// #define) 不是 - 所问的字面问题对其他人来说很有趣和有用。重叠是不幸的,但这里有一个独特的问题很有价值。它不应该被关闭。
  • @MarkAmery:标题是'XY Problem'——用户试图实现的解决方案是在指定的副本中,但问题是要求一些切线的东西,甚至无法实现他们想要。
  • @JonathanLeffler 不管你是否认为这个标题有问题,如果我没有在谷歌搜索结果中点击这个标题,我根本不会找到这些答案或想以某种方式做事。对我来说,问题更多是关于如何在不需要 #ifdefs 的情况下从生产环境中剥离代码,并且感谢 OP 的切题问题,我找到了让我走上正轨的想法。我不会搜索关于“调试打印”的问题——这根本不是我的用例!

标签: c debugging c-preprocessor


【解决方案1】:

不,你不能。在开始处理预处理指令之前,从代码中删除注释。因此,您不能在宏中包含注释。

此外,以后使用任何宏诡计“形成”评论的任何尝试都不能保证有效。编译器不需要将“迟到”的 cmets 识别为 cmets。

实现您想要的最佳方式是在 C99 中使用带有可变参数的宏(或者,也许,使用编译器扩展)。

【讨论】:

    【解决方案2】:

    一个常见的技巧是这样做:

    #ifdef DEBUG
      #define OUTPUT(x) printf x
    #else
      #define OUTPUT(x)
    #endif
    
    #include <stdio.h>
    int main(void)
    {   
      OUTPUT(("%s line %i\n", __FILE__, __LINE__));
    
      return 0;
    }
    

    这样您就可以使用printf() 的全部功能,但您必须忍受双括号才能使宏工作。

    双括号的要点是:你需要一组来表明它是一个宏调用,但你不能在 C89 中的宏中包含不确定数量的参数。但是,通过将参数放在它们自己的一组括号中,它们会被解释为单个参数。在定义DEBUG 时扩展宏时,替换文本是单词printf 后跟singl 参数,实际上是括号中的几项。然后括号将被解释为printf 函数调用中所需的括号,因此一切正常。

    【讨论】:

    • 另外,你可以使用 OUTPUT(x...) printf(x) 来避免双 ()。
    • @Brian 如果您想要可变数量的参数,这将不起作用。有关详细信息,请参阅说明文本。
    【解决方案3】:

    С99方式:

    #ifdef DEBUG
        #define printd(...) printf(__VA_ARGS__)
    #else
        #define printd(...)
    #endif
    

    好吧,这个不需要 C99,但假设编译器已为发布版本打开优化:

    #ifdef DEBUG
        #define printd printf
    #else
        #define printd if (1) {} else printf
    #endif
    

    【讨论】:

    • 正确,但不太便携。
    • 好多了,但“便携式”版本再次不会禁用对参数的有效性检查。 Tim 的回答中给出了在非 C99 实现中执行此操作的正确方法。
    • 我认为这是它好的一面。为什么要禁用调试版本中真实代码的有效性检查?
    • 简单:因为该代码在发布版本中可能无效。调试代码在发布中无效并没有错。而且,大多数时候都是这样。
    • @AndreyT:尽管这个问题已经很老了并且已经得到了回答,但我对你的评论很感兴趣。您能否提供或指出调试代码在发布版本中可能无效的示例?非常感谢。
    【解决方案4】:

    在某些编译器(包括 MS VS2010)上可以使用,

    #define CMT / ## /
    

    但没有所有编译器的受赠者。

    【讨论】:

      【解决方案5】:

      您可以将所有调试调用放在一个函数中,将其命名为printf_debug 并将DEBUG 放在该函数中。 编译器会优化空函数。

      【讨论】:

      • 谢谢你,终于理智了……除了它主要是预处理器的工作。
      • 在这种情况下你可以调用你的函数 printd
      • 这不会禁用对参数的评估,也不会禁用对参数有效的要求(例如,声明)。换句话说,它不执行请求的操作。
      • @AndreyT,如果完成任何类型的链接时间优化,编译器将内联空函数并删除用于评估参数的死代码。
      • 问题是如果参数在发布上下文中无效,它根本不会编译。它不会进行“优化”。
      【解决方案6】:

      标准方式是使用

      #ifndef DEBUG
          #define printd(fmt, ...)  do { } while(0)
      #else
          #define printd(fmt, ...) printf(fmt, __VA_ARGS__)
      #endif
      

      这样,当您在末尾添加分号时,它会执行您想要的操作。 由于没有操作,编译器将编译出“do...while”

      【讨论】:

        【解决方案7】:

        未经测试: 编辑:经过测试,现在自己使用它:)

        #define DEBUG 1
        #define printd(fmt,...) if(DEBUG)printf(fmt, __VA_ARGS__)
        

        要求你不仅要定义DEBUG,还要给它一个非零值。

        附录: 也适用于std::cout

        【讨论】:

          【解决方案8】:

          在 C++17 中,我喜欢将 constexpr 用于类似的事情

          #ifndef NDEBUG
          constexpr bool DEBUG = true;
          #else
          constexpr bool DEBUG = false;
          #endif
          

          那你就可以了

          if constexpr (DEBUG) /* debug code */
          

          需要注意的是,与预处理器宏不同,您的范围有限。您既不能在一个调试条件中声明可从另一个调试条件访问的变量,也不能在外部函数范围内使用它们。

          【讨论】:

          • 这不是答案,问题标记为 C,答案是 C++。这是两种不同的语言。
          【解决方案9】:

          您可以利用if。例如,

          #ifdef debug
              #define printd printf
          #else 
              #define printd if (false) printf
          #endif
          

          如果您设置像-O2 这样的优化标志,编译器将删除这些无法访问的代码。这个方法对于std::cout也很有用。

          【讨论】:

            【解决方案10】:

            正如 McKay 所指出的,如果您只是尝试将 printd 替换为 //,则会遇到问题。相反,您可以使用可变参数宏将 printd 替换为不执行任何操作的函数,如下所示。

            #ifndef DEBUG
                #define printd(...) do_nothing()
            #else
                #define printd(...) printf(__VA_ARGS__)
            #endif
            
            void do_nothing() { ; }
            

            使用像 GDB 这样的调试器也可能会有所帮助,但有时快速的printf 就足够了。

            【讨论】:

              【解决方案11】:

              我经常使用这个结构:

              #define DEBUG 1
              #if DEBUG
              #if PROG1
              #define DEBUGSTR(msg...)        { printf("P1: "); printf( msg); }
              #else
              #define DEBUGSTR(msg...)        { printf("P2: "); printf( msg); }
              #endif
              #else
              #define DEBUGSTR(msg...)    ((void) 0)
              #endif
              

              这样我可以在我的控制台中告诉我哪个程序给出了哪个错误消息...而且,我可以轻松搜索我的错误消息...

              就我个人而言,我不喜欢 #defining 只是表达式的一部分......

              【讨论】:

                【解决方案12】:

                已经完成了。我不推荐它。没时间测试,但机制是这样的:

                 #define printd_CAT(x) x ## x
                 #ifndef DEBUG
                    #define printd printd_CAT(/)
                 #else
                    #define printd printf
                 #endif
                

                如果您的编译器在编译器本身中处理 // cmets,则此方法有效(没有像 ANSI 那样保证 /* cmets 有两次传递的保证)。

                【讨论】:

                  猜你喜欢
                  • 2010-09-13
                  • 2016-12-30
                  • 2011-01-21
                  • 1970-01-01
                  • 2021-09-29
                  • 2010-12-09
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多