C ++中#define指令的目的是什么?

时间:2010-05-10 20:47:55

标签: c++ c-preprocessor

#define指令的作用是什么?

8 个答案:

答案 0 :(得分:14)

#define用于在C和C ++中创建宏。您可以在C preprocessor documentation中详细了解相关信息。快速回答是它做了一些事情:

  1. 简单宏 - 基本上只是文本替换。编译时间常数就是一个很好的例子:

    #define SOME_CONSTANT 12
    

    只需将文字SOME_CONSTANT替换为代码中出现的12。这种宏通常用于提供代码块的条件编译。例如,项目中的每个源文件可能包含一个标头,其中包含项目选项列表:

    #define OPTION_1
    #define OPTION_2
    #undef  OPTION_3
    

    然后项目中的代码块将包含匹配的#ifdef / #endif#块,以在已完成的项目中启用和禁用这些选项。使用-D gcc标志会提供类似的行为。但是,对于该方法是否真的是一种为应用程序提供配置的好方法,存在强烈的意见。

  2. 带参数的宏 - 允许您创建可以接受参数并操纵它们的“类似函数”的宏。例如:

    #define SQUARE(x)  ((x) * (x))
    

    会返回参数的平方作为结果;注意潜在的操作顺序或副作用问题!以下示例:

    int x = SQUARE(3);     // becomes int x = ((3) * (3));
    

    可行,但有点像:

    int y = SQUARE(f());   // becomes int y = ((f()) * (f()));
    

    会两次致电f(),甚至更糟:

    int z = SQUARE(x++);   // becomes int z = ((x++) * (x++));
    

    导致未定义的行为!

    使用某些工具,带参数的宏也可以是variadic,这可以派上用场。

  3. 正如下面评论中所提到的,过度使用宏,或过度复杂或令人困惑的宏的开发被许多人认为是不好的风格 - 一如既往,将代码的可读性,可维护性和可调试性置于'聪明'技术诀窍之上

答案 1 :(得分:7)

#define(相反,它是#undef)可用于设置编译器指令,然后可以使用#ifndef或#ifdef对其进行测试。这允许在源文件中定义自定义行为。它通常用于编译不同的环境或调试代码。

一个例子:

#define DEBUG



#ifdef DEBUG

//perform debug code

#endif

答案 2 :(得分:2)

#define最常见的用途()适用于包含警员:

// header.hh
#ifndef HEADER_HH_
#define HEADER_HH_

namespace pony {
// ...
}

#endif

#define的另一个常见用途是创建配置文件,通常是config.h文件,其中我们#define宏基于各种状态和条件。然后,在我们的代码中,我们使用#ifdef#elif defined()等来测试这些宏,以支持针对不同情况的不同编译。这不像include-guard惯用法那么坚固,你需要在这里小心,因为如果分支错误,那么你可以得到非常模糊的编译器错误,或者更糟糕的是,运行时行为。

通常,除了包含保护之外,您需要仔细考虑(最好两次)关于问题,并查看是否可以使用编译器而不是预处理器来解决它。编译器比预处理器更聪明。不仅如此,编译器不可能混淆预处理器,而预处理器肯定会混淆和误导编译器。

答案 3 :(得分:1)

#define指令有两个常见用途。

第一个是控制编译器的行为方式。为此,我们还需要#undef,#ifdef和#ifndef。 (和#endif也......)

您可以通过这种方式制作“编译逻辑”。一个常见的用途是激活或不激活代码的调试部分,如:

#ifdef DEBUG

//debug code here

#endif

您可以通过编写#define DEBUG来编译调试代码

这种逻辑的另一个用途是避免双重包含...

示例,文件A,#包含文件B和C.但文件B也包含C.这可能会导致编译错误,因为“C”存在两次。

解决方案是写:

#ifndef C_FILE_INCLUDED
#define C_FILE_INCLUDED

//the contents of header "c" go here.

#endif

#define的另一个用途是制作宏。

最简单的,包括简单的替换,如:

#define PI 3.14159265

float perimeter(float radius) {
    return radius*2*PI;
}

#define SHOW_ERROR_MESSAGE printf("An serious error happened");

if ( 1 != 1 ) { SHOW_ERROR_MESSAGE }

然后你也可以创建接受参数的宏,printf本身通常是一个宏,在头文件中用#define创建。

但这不应该做,因为有两个原因: 首先,速度os宏,与使用内联相同,其次,我们有c ++模板,允许更多地控制变量类型的函数。因此,使用带参数的宏的唯一原因是制作奇怪的结构,以后很难理解,就像元编程的东西......

答案 4 :(得分:1)

在C ++中,#define具有非常狭窄的专业角色:

  • 其他答案中描述的头部警卫
  • 与标准库交互。例如,在包含windows.h之前#defining WINDOWS_LEAN_AND_MEAN会关闭像MAX这样经常出问题的宏。
  • 涉及字符串化的高级宏(即打印调试消息的宏)或标记粘贴。

您应避免使用#define用于以下目的。原因很多;见instace this FAQ entry

  • 编译时常数。请改用const
  • 简单的宏功能。请改用inline函数和模板。

答案 5 :(得分:0)

C C ++ #define 中的

允许您创建预处理器宏。

在正常的 C C ++ 构建过程中,首先发生的事情是PreProcessor运行,预处理器查看预处理器指令的源文件,如 #define #include ,然后使用它们执行简单的操作。

#define 指令的情况下,预处理器执行简单的基于文本的替换。

例如,如果你有代码

#define PI 3.14159f

float circum = diameter*PI;

预处理器会把它变成:

float circum = diameter* 3.14159;

只需将PI的实例替换为相应的文本即可。对于更高级的用途,这只是 #define 语句的最简单形式,请从MSDN中查看此article

答案 6 :(得分:0)

inCorrectUseOfHashDefine()

{

#define的作用是阻止那些使用以下蓝色语句继承你代码的人:

foreverandever

因为:

#define foreverandever for(;;)

}

请优先选择常规#define。

它还用于设置编译器指令......

答案 7 :(得分:0)

关于#defines的大部分内容已经被告知,但目前尚不清楚C ++在大多数用途中有更好的替代品:

  1. 定义数值常量的#define可以很容易地被const“变量”替换,作为#define,它在编译的可执行文件中并不存在。 AFAIK几乎可以用于所有可以使用#defined数值常量的情况,包括数组边界。对我来说主要的优点是这些常量显然是类型化的,因​​此不需要在宏中添加强制转换“只是为了确定”,并且是有范围的,因此它们可以保存在命名空间/类/函数中,而不会污染所有的应用
  2. const int max_array_size=50;
    int an_array[max_array_size];
    
    1. #define来创建宏:宏通常可以被模板替换;例如,可怕的MAX宏
    2. #define MAX(a,b)    ((a)<(b)?(b):(a))
      

      ,有几个缺点(例如重复参数评估,不可避免的内联扩展),可以用max函数替换

      template<typename T> T & max(T & a, T & b)
      {
          return a<b?b:a;
      }
      

      可以是类型安全的(在这个版本中,两个参数被强制为相同的类型),可以内联扩展,也可以不扩展(它是编译器决定),只评估一次参数(当它被调用时) ,并且是范围的。可以找到更详细的解释here

      尽管如此,宏还必须用于包含保护,以创建某种奇怪的语言扩展,扩展到更多代码行,具有不平衡的括号等。