【问题标题】:What use cases necessitate #define without a token-string?哪些用例需要 #define 没有令牌字符串?
【发布时间】:2011-11-27 12:45:29
【问题描述】:

我之前在学习C的时候遇到过#define预处理指令,后来在我看的一些代码中也遇到过。但除了用它来明确替换常量和定义宏之外,我还没有真正理解在没有“主体”或标记字符串的情况下使用它的特殊情况。

以这一行为例:

#define OCSTR(X)

就这样!这个或更好的用途可能是什么,什么时候需要使用#define

【问题讨论】:

  • 这可以与另一组允许条件编译的指令结合使用。没有标记字符串的#define 会从源文件中删除标识符的出现。标识符保持定义状态,可以使用 #if defined 和 #ifdef 指令进行测试。

标签: c++ c c-preprocessor


【解决方案1】:

这用于两种情况。第一个也是最常见的涉及 条件编译:

#ifndef XYZ
#define XYZ
//  ...
#endif

你自己肯定用过这个来包含守卫,但它也可以是 用于系统依赖之类的东西:

#ifdef WIN32
//  Windows specific code here...
#endif

(在这种情况下,WIN32 更可能是在命令行中定义的,但它 也可以在"config.hpp" 文件中定义。)这通常会 只涉及类似对象的宏(没有参数列表或 括号)。

第二个是条件编译的结果。某物 喜欢:

#ifdef DEBUG
#define TEST(X) text(X)
#else
#define TEST(X)
#endif

这允许编写如下内容:

TEST(X);

如果定义了DEBUG,它将调用该函数,如果它不做任何事情 不是。

【讨论】:

  • 接受这个不是因为其他人不正确,而是因为它为正确答案增加了更多的说明。谢谢
【解决方案2】:

这样的宏通常成对出现在条件#ifdef中,如:

#ifdef _DEBUG
   #define OCSTR(X)
#else
   #define OCSTR(X)  SOME_TOKENS_HERE
#endif

另一个例子,

#ifdef __cplusplus
   #define NAMESPACE_BEGIN(X) namespace X {
   #define NAMESPACE_END }
#else
   #define NAMESPACE_BEGIN(X) 
   #define NAMESPACE_END
#endif

【讨论】:

    【解决方案3】:

    我最近挖出一个奇怪的案例来回答一个问题,结果证明这只是评论性质的。有问题的代码如下所示:

    void CLASS functionName(){
      //
      //
      //
    }
    

    我发现它只是一个空的#define,作者选择记录该函数访问项目中的全局变量:

    C++ syntax: void CLASS functionName()?

    所以和/* CLASS */ 没什么不同,除了不允许出现像/* CLAAS */ 这样的拼写错误……也许还有其他一些小好处(?)

    【讨论】:

      【解决方案4】:

      我同意每个答案,但我想指出一件小事。

      作为一个 C 纯粹主义者,我从小就坚信每个和每个 #define 都应该是一个表达式,所以,即使它是一种常见的做法:

      #define WHATEVER
      

      并用它测试它

      #ifdef WHATEVER
      

      我认为写作总是更好:

      #define WHATEVER (1)
      

      #debug 宏也应该是表达式:

      #define DEBUG (xxx) (whatever you want for debugging, value)
      

      通过这种方式,您可以完全避免滥用#macros 并防止出现令人讨厌的问题(尤其是在一个 1000 万行的 C 项目中)

      【讨论】:

      • 这场#if#ifdef 的辩论可能会让你感兴趣,我个人站在#if stackoverflow.com/questions/135069/… 一边
      • 想到了有效使用#if 0 来注释掉整段代码:D。顺便说一句,辩论很有趣。像往常一样,做事没有正确或错误的方式,这取决于你在做什么。我会尽快阅读。谢谢。
      • 在我的例子中,当我有 #define TEST 没有表达式时,#elif TEST 宏会抛出一个错误,说 error: expected value in expression 在铿锵声中。
      【解决方案5】:

      当您可能想要使某些功能静音时,可以使用它。例如,在调试模式下,您想打印一些调试语句,而在生产代码中,您想省略它们:

      #ifdef DEBUG
      #define PRINT(X) printf("%s", X)
      #else
      #define PRINT(X)  // <----- silently removed
      #endif
      

      用法:

      void foo ()
      {
        PRINT("foo() starts\n");
        ...
      }
      

      【讨论】:

      • 您的回答(以及许多其他人的回答)是正确的,但您的示例很危险。如果在 PRINT(X) 之前存在没有封闭 { } 的 if() 怎么办?致读者:请注意,这只是一个简单的示例,请勿按原样使用。
      • 这就是为什么在 C/C++ 中每个宏都应该是一个表达式,只是为了避免这种情况。问候。
      【解决方案6】:

      #define 宏在预处理期间被它们的替换文本简单地替换。如果没有替换文本,那么......它们被什么都替换了!所以这个源代码:

      #define FOO(x)
      
      print(FOO(hello world));
      

      将被预处理成这样:

      print();
      

      这对于删除你不想要的东西很有用,比如assert()。它主要在有条件的情况下有用,但在某些情况下有一个非空的主体。

      【讨论】:

        【解决方案7】:

        正如您在上面的回复中看到的,它在调试代码时很有用。

        #ifdef DEBUG
        #define debug(msg) fputs(__FILE__ ":" (__LINE__) " - " msg, stderr)
        #else
        #define debug(msg)
        #endif
        

        所以,当你在调试时,函数会打印行号和文件名,这样你就知道是否有错误。如果你不调试,它只会产生任何输出

        【讨论】:

          【解决方案8】:

          这种东西有很多用途。

          例如,一个是宏在不同的构建中具有不同的行为。例如,如果你想要调试消息,你可以有这样的东西:

          #ifdef _DEBUG
            #define DEBUG_LOG(X, ...) however_you_want_to_print_it
          #else
            #define DEBUG_LOG(X, ...) // nothing
          #endif
          

          另一个用途是根据您的系统自定义头文件。这是来自我在 linux 中的台面实现的 OpenGL 标头:

          #if !defined(OPENSTEP) && (defined(__WIN32__) && !defined(__CYGWIN__))
          #  if defined(__MINGW32__) && defined(GL_NO_STDCALL) || defined(UNDER_CE)  /* The generated DLLs by MingW with STDCALL are not compatible with the ones done by Microsoft's compilers */
          #    define GLAPIENTRY 
          #  else
          #    define GLAPIENTRY __stdcall
          #  endif
          #elif defined(__CYGWIN__) && defined(USE_OPENGL32) /* use native windows opengl32 */
          #  define GLAPIENTRY __stdcall
          #elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 303
          #  define GLAPIENTRY
          #endif /* WIN32 && !CYGWIN */
          
          #ifndef GLAPIENTRY
          #define GLAPIENTRY
          #endif
          

          并用于标头声明中,例如:

          GLAPI void GLAPIENTRY glClearIndex( GLfloat c );
          
          GLAPI void GLAPIENTRY glClearColor( GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha );
          
          GLAPI void GLAPIENTRY glClear( GLbitfield mask );
          
          ...
          

          (我删除了GLAPI的部分)

          所以你得到了图片,在某些情况下使用但在其他情况下不使用的宏可以定义为这些情况下的某些内容,而其他情况下则不定义任何内容。

          其他情况如下:

          如果宏不带参数,则可能只是为了声明某种情况。一个著名的例子是保护头文件。另一个例子是这样的

          #define USING_SOME_LIB
          

          以后可以这样使用:

          #ifdef USING_SOME_LIB
          ...
          #else
          ...
          #endif
          

          可能是宏在某个阶段被用来做某事(例如日志),但在发布时,所有者决定日志不再有用,并简单地删除了宏的内容,使其变为空。不过不建议这样做,使用我在答案开头提到的方法。

          最后,它可能只是为了更多的解释,例如你可以说

          #define DONT_CALL_IF_LIB_NOT_INITIALIZED
          

          然后你编写如下函数:

          void init(void);
          void do_something(int x) DONT_CALL_IF_LIB_NOT_INITIALIZED;
          

          虽然这最后一种情况有点荒谬,但在这种情况下还是有道理的:

          #define IN
          #define OUT
          
          void function(IN char *a, OUT char *b);
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-01-23
            • 2017-07-28
            • 1970-01-01
            • 1970-01-01
            • 2013-03-20
            • 2014-05-14
            相关资源
            最近更新 更多