C ++中的宏和const有什么区别?

时间:2011-06-18 04:04:55

标签: c++ macros const

我在技术访谈中被问到这个问题:

  

C ++中的const和宏之间有什么区别?

我的回答是宏是一个预处理器指令,如果使用宏,可能很难调试应用程序,因为它在编译之前被常量表达式替换,而const可以有一个类型标识符并且易于调试。

任何人都可以指出任何其他差异,哪些应该是首选的?

修改

来自C ++的IBM文档:

  

以下是#defineconst类型限定符之间的一些差异:

     
      
  1. #define指令可用于为数字,字符或字符串常量创建名称,而可以声明任何类型的const对象。

  2.   
  3. const对象遵循变量的范围规则,而使用#define创建的常量则不受影响。与const对象不同,宏的值不会出现在编译器使用的中间源代码中,因为它们是内联扩展的。内联扩展使得调试器无法使用宏值。

  4.   
  5. 宏可用于常量表达式,例如数组绑定,而const对象则不能。 (我认为我们肯定需要使用宏来定义array_size

  6.   
  7. 编译器不会对宏进行类型检查,包括宏参数。

  8.   

7 个答案:

答案 0 :(得分:24)

宏和常量并不是一回事,每个都有时适合于这种情况,而你的答案只是划分了表面上的差异。此外,C ++有两种不同的常量。

使用const限定符定义的常量最好被认为是不可修改的变量 。它具有变量的所有属性:它有一个类型,它有一个大小,它有链接,你可以取其地址。 (编译器可能会优化掉其中的一些属性,如果它可以侥幸逃脱:例如,从未使用过地址的常量可能不会被发送到可执行映像中。但这只能通过as-if规则的优雅。 )你不能对const数据做的唯一改变是它的值。用enum定义的常量略有不同。它有一个类型和一个大小,但它没有链接,你不能采取它的地址,它的类型是唯一的。这两个都在翻译阶段7中处理,因此它们不能是左值或左值。 (对于上一句中的行话我很抱歉,但我不得不写几段。)

宏的约束要少得多:它可以扩展到任何令牌序列,只要整个程序仍然是一个格式良好的程序。它没有变量的任何属性。将sizeof&应用于宏可能会也可能不会执行某些有用的操作,具体取决于宏扩展到的内容。宏有时被定义为扩展为数字文字,而这些宏有时认为为常量,但它们不是:“编译器本身”(即转换阶段7)将它们视为< em>数字文字。

如今,通常认为良好的做法是在常量不执行时使用宏。宏不遵循与所有其他标识符相同的作用域规则,这可能会造成混淆,如果使用常量,则会向转换阶段7提供更多信息,从而也向调试器提供更多信息。但是,宏允许你做任何其他方式无法完成的事情,如果你需要做其中一件事,你应该毫不犹豫地使用它们。 (在这种意义上,正在拉动它们的重量的宏通常会只扩展到数字文字,但我不会说永远不会。)

编辑:以下是宏做一些有趣事情的示例。绝不是形状或形成常数。可能有一种方法可以在没有宏的情况下获得相同的效果(如果你知道一个不涉及字符串流的话,我会好奇地听到它!)但我认为它很好地说明了它的功能和宏的危险(对于后者,考虑如果在一个非常具体的背景之外使用它将会做什么......)

static double elapsed()
{ ... }
#define ELAPSED '[' << std::fixed << std::setprecision(2) << elapsed() << "] "

// usage:
for (vector<string>::iterator f = files.begin(); f != files.end(); f++) {
    cout << ELAPSED << "reading file: " << *f << '\n';
    process_file(*f);
}

答案 1 :(得分:12)

由于多种原因,我应该更喜欢const int sum = 1;而不是#define sum 1

基于范围的机制:

#define不尊重范围,因此无法创建类范围的命名空间。虽然const变量可以在类中作用域。

在编译错误期间避免奇怪的魔法数字:

如果您正在使用#define那些在预编译时被预处理器取代那么如果您在编译期间收到错误,那将会引起混淆,因为错误消息不会引用宏名称而是值和它会出现突然的价值,而且会浪费很多时间在代码中跟踪它。

易于调试:

同样出于同样的原因,虽然调试#define实际上没有提供任何帮助 为了避免上述两种情况,const将是更好的选择。

答案 2 :(得分:2)

另一个区别是const变量有一个内存,可以通过指针引用。宏只是在编译之前会发生的自动完成,因此在编译期间名称会丢失。

宏也可以只是一个常数。它可以是表达式或任何语法正确的东西,甚至是函数的完整定义。

宏用于描述编程选择,例如堆栈大小;而cosnt用于描述真实世界常数,如Pi或e的值。

答案 3 :(得分:2)

(最初发布于static const vs #define - 在这里复制,因为这个问题似乎有更多的“动力”......让我知道这是不合适的......)

根据使用情况,对一切事物的利弊:

  • consts
    • 正确处理了适当范围/标识符冲突问题
    • 强大,单一,用户指定的类型
      • 您可能会尝试“键入”#define ala #define S std::string("abc"),但常量会避免在每个使用点重复构建不同的临时工具
    • 一个定义规则并发症
    • 可以获取地址,创建对它们的const引用等。
  • 定义
    • “全局”范围/更容易出现冲突的用法,这会产生难以解决的编译问题和意外的运行时结果,而不是明智的错误消息;减轻这种情况需要:
      • 长,模糊和/或集中协调的标识符,对它们的访问不能从隐式匹配used / current / Koenig-looking-up命名空间,命名空间别名等中受益。
      • 通常需要使用所有大写字符并为预处理器定义保留(企业规模预处理器使用以保持可管理的重要指南,以及可以预期哪些第三方库可以遵循),观察其中意味着迁移现有的consts或定义的枚举涉及大写的变化(因此影响客户代码)。 (就我个人而言,我将枚举的第一个字母大写,但不是有效的,所以无论如何我都会被打到这里 - 也许是时候重新考虑一下了。)
    • 更多可能的编译时操作:字符串文字串联,字符串化(取其大小)
      • 缺点是给定#define X "x"和一些客户端使用ala "pre" X "post",如果你想要或需要使X成为运行时可更改的变量而不是常量,那么你会遇到麻烦,而那个过渡是更容易从const char*const std::string,因为他们已经强制用户合并连接操作。
    • 不能直接在已定义的数字常量上使用sizeof
    • 无类型(如果与无符号相比,GCC不会发出警告)
    • 某些编译器/链接器/调试器链可能不会显示标识符,因此您将被缩减为查看“魔术数字”(字符串,等等......)
    • 无法取得地址
    • 在创建#define的上下文中,替换值不一定合法(或离散),因为它在每个使用点进行评估,因此您可以引用尚未声明的对象,取决于“实现”不需要预先包含,创建“常量”,如{ 1, 2 }可用于初始化数组,或#define MICROSECONDS *1E-6等(绝对不推荐这个!)< / LI>
    • __FILE____LINE__等特殊内容可以合并到宏替换
  • 枚举
    • 仅适用于整数值
    • 正确处理了适当范围/标识符冲突问题
    • 强类型,但是对于没有控制权的大小的有符号或无符号的int大小(在C ++ 03中)
    • 无法取得地址
    • 更强的使用限制(例如递增 - template <typename T> void f(T t) { cout << ++t; }将无法编译)
    • 从封闭枚举中获取的每个常量类型,因此template <typename T> void f(T)在从不同枚举传递相同数值时获得不同的实例化,所有这些都与任何实际的f(int)实例化不同。
    • 即使使用typeof,也不能指望numeric_limits提供有用的洞察力
    • enum的typename可能出现在RTTI,编译器消息等各个地方 - 可能有用,可能是混淆

作为一般规则,我使用consts并认为它们是一般用法的最专业选项(尽管其他人对这个老惰性程序员有一种简单的吸引力)。

答案 4 :(得分:0)

宏不遵守范围,并且宏的名称可能无法用于符号调试器。 Dan Saks有一篇关于宏(无),常量对象和枚举常量的相对优点的相当完整的文章。与Stephen Dewhurst类似,Saks更喜欢整数值的枚举常量,因为它们不占用存储空间(更准确地说,枚举常量既没有存储持续时间也没有链接)。

答案 5 :(得分:0)

define可以重新定义,但const会导致编译错误:

样品: 来源:main.cpp

#define int_constance 4
#define int_constance 8 // ok, compiler will warning ( redefine macro)

const int a = 2;
const int a = 4; // redefine -> error

int main(int argc, char** argv)
{
   std::cout << int_constance ; // if remove second #define line, output will be 8

   return 0;
}

答案 6 :(得分:-1)

宏总是有一个类型,例如,#define FIVE 5的类型为int。

const变量优于宏的优点可能是内存使用:对于宏,值可能必须在任何地方重复使用,const变量不会在内存中重复。 (但我不确定这种差异)