时间:2011-09-29 08:56:03

标签: c++ c c-preprocessor

我在学习C之前遇到过#define预处理器指令,然后在我读过的一些代码中遇到过它。但除了使用它来定义常量的替换和定义宏之外,我还没有真正解决使用它而没有“body”或token-string的特殊情况。

以这一行为例:

#define OCSTR(X)

就是这样!什么可以使用这个或更好,什么时候使用#define必要?

8 个答案:

答案 0 :(得分:7)

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

#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,它将调用该函数,如果没有则不执行任何操作 不是

答案 1 :(得分:3)

答案 2 :(得分:2)

我最近挖出来回答一个问题的一个奇怪的案例,结果只是简单的评论。有问题的代码如下:

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

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

C++ syntax: void CLASS functionName()?

所以与/* CLASS */如果不同,除了不允许像/* CLAAS */这样的拼写错误之外没有什么不同......或许其他一些小的好处(?)

答案 3 :(得分:0)

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

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

用法:

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

答案 4 :(得分:0)

在预处理过程中,

#define宏在字面上被替换文本替换。如果没有替换文本,那么...它们将被替换为什么!所以这个源代码:

#define FOO(x)

print(FOO(hello world));

将被预处理为:

print();

这可以用来摆脱你不想要的东西,比如说assert()。它主要适用于条件情况,但在某些条件下,有一个非空体。

答案 5 :(得分:0)

正如您在上面的响应中所看到的,它在调试代码时非常有用。

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

因此,在进行调试时,该函数将打印行号和文件名,以便您知道是否有错误。如果你没有调试,它只会产生无输出

答案 6 :(得分:0)

这样的事情有很多用途。

例如,一个是宏在不同的构建中具有不同的行为。例如,如果您需要调试消息,可以使用以下内容:

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

另一种用途可能是根据您的系统自定义您的头文件。这是我在linux中的mesa实现的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);

答案 7 :(得分:0)

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

作为C纯粹主义者,我已经成长为断言每个#define应该是一个表达式,因此,即使通常的做法是使用:

#define WHATEVER

并使用

进行测试
#ifdef WHATEVER

我认为写作总是更好:

#define WHATEVER (1)

#debug宏也应该是表达式:

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

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