我在技术访谈中被问到这个问题:
C ++中的
const
和宏之间有什么区别?
我的回答是宏是一个预处理器指令,如果使用宏,可能很难调试应用程序,因为它在编译之前被常量表达式替换,而const
可以有一个类型标识符并且易于调试。
任何人都可以指出任何其他差异,哪些应该是首选的?
修改
来自C ++的IBM文档:
以下是
#define
和const
类型限定符之间的一些差异:
#define
指令可用于为数字,字符或字符串常量创建名称,而可以声明任何类型的const对象。const对象遵循变量的范围规则,而使用
#define
创建的常量则不受影响。与const
对象不同,宏的值不会出现在编译器使用的中间源代码中,因为它们是内联扩展的。内联扩展使得调试器无法使用宏值。宏可用于常量表达式,例如数组绑定,而
const
对象则不能。 (我认为我们肯定需要使用宏来定义array_size
。- 醇>
编译器不会对宏进行类型检查,包括宏参数。
答案 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 - 在这里复制,因为这个问题似乎有更多的“动力”......让我知道这是不合适的......)
根据使用情况,对一切事物的利弊:
#define
ala #define S std::string("abc")
,但常量会避免在每个使用点重复构建不同的临时工具#define X "x"
和一些客户端使用ala "pre" X "post"
,如果你想要或需要使X成为运行时可更改的变量而不是常量,那么你会遇到麻烦,而那个过渡是更容易从const char*
或const std::string
,因为他们已经强制用户合并连接操作。{ 1, 2 }
可用于初始化数组,或#define MICROSECONDS *1E-6
等(绝对不推荐这个!)< / LI>
__FILE__
和__LINE__
等特殊内容可以合并到宏替换template <typename T> void f(T t) { cout << ++t; }
将无法编译)template <typename T> void f(T)
在从不同枚举传递相同数值时获得不同的实例化,所有这些都与任何实际的f(int)实例化不同。作为一般规则,我使用consts并认为它们是一般用法的最专业选项(尽管其他人对这个老惰性程序员有一种简单的吸引力)。
答案 4 :(得分:0)
宏不遵守范围,并且宏的名称可能无法用于符号调试器。 Dan Saks有一篇关于宏(无),常量对象和枚举常量的相对优点的相当完整的文章。与Stephen Dewhurst类似,Saks更喜欢整数值的枚举常量,因为它们不占用存储空间(更准确地说,枚举常量既没有存储持续时间也没有链接)。
答案 5 :(得分:0)
样品: 来源: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变量不会在内存中重复。 (但我不确定这种差异)