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

| 在技​​术面试中有人问我这个问题:   C ++中的ѭ0和宏有什么区别? 我的回答是,宏是预处理器指令,如果使用宏,则可能很难调试应用程序,因为在编译之前它已被常量表达式替换,而
const
可以具有类型标识符并且易于调试。 任何人都可以指出任何其他区别,应该优先选择哪个? 编辑: 从IBM C ++文档中:   以下是
#define
const
类型限定符之间的一些区别:         
#define
指令可用于为数字,字符或字符串常量创建名称,而可以声明任何类型的const对象。   const对象服从变量的作用域规则,而使用
#define
创建的常数则不受约束。与“ 0”对象不同,宏的值不会出现在编译器使用的中间源代码中,因为它们是内联扩展的。内联扩展使宏值对于调试器不可用。   可以在常量表达式(如数组绑定)中使用宏,而
const
对象则不能。 (我认为我们肯定需要使用宏来定义
array_size
。   编译器不会对宏(包括宏参数)进行类型检查。        
已邀请:
        宏和常量并不是完全相同的东西,有时每个宏和常量都适合于这种情况,并且您的答案仅是表面上的区别而已。同样,C ++具有两种不同的常量。 最好将用“ 0”限定符定义的常量视为不可修改的变量。它具有变量的所有属性:它具有类型,具有大小,具有链接,可以使用其地址。 (如果编译器可以摆脱这些属性,它们可能会优化其中的一些属性:例如,从未使用过地址的常量可能不会被释放到可执行映像中。但这只是通过as-if规则的宽限期。 )对
const
基准唯一不能做的就是更改其值。用
enum
定义的常数有些不同。它具有类型和大小,但是没有链接,您不能获取其地址,并且其类型是唯一的。这两个都是在翻译阶段7中处理的,因此它们只能是左值或右值。 (对于前一句中的术语,我感到抱歉,但否则我将不得不写几段。) 宏的约束要少得多:只要整个程序仍然是格式正确的程序,它就可以扩展到任何令牌序列。它没有变量的任何属性。对宏应用
sizeof
&
可能会或可能不会做有用的事情,具体取决于宏扩展到的内容。有时将宏定义为扩展为数字文字,并且有时将此类宏视为常量,但它们不是:\“编译器固有”(即翻译阶段7)将它们视为数字文字。 如今,通常认为良好的做法是在常量可用时不要使用宏。宏不遵循与所有其他标识符相同的作用域规则,这可能会造成混淆,并且,如果您使用常量,则会为转换阶段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);
}
    
出于多种原因,一个人应该比ѭ16prefer更喜欢ѭ15:: 基于范围的机制: “ 2”不遵守范围,因此无法创建类范围的命名空间。虽然const变量可以在类中定义范围。 在编译错误期间避免怪异的幻数: 如果您使用的是“ 2”,则在预编译时将其替换为预处理器。因此,如果您在编译过程中收到错误,则会感到困惑,因为错误消息不会引用宏名称,但是该值会出现一个突然的值,而且会浪费大量时间在代码中进行跟踪。 易于调试: 同样出于同样的原因,调试
#define
并不会提供任何帮助。 为避免上述两种情况,
const
将是一个更好的选择。     
        另一个不同是变量“ 0”具有内存,可以由指针引用。宏只是在编译之前会发生的自动完成功能,因此名称在编译过程中会丢失。 宏也可以不仅仅是一个常数。它可以是表达式,也可以是语法上正确的任何东西,甚至可以是函数的整个定义。 宏用于描述编程选择,例如堆栈大小;而ѭ22to则用来描述真实世界的常数,例如Pi或e的值。     
(最初是为静态const vs #define发布的-在此复制,因为这个问题似乎有更多的“动量” ...让我知道这是否不合适...) 优点和缺点,取决于用法: consts 正确确定范围/标识符冲突问题得到了很好的处理 强,单一,用户指定的类型 您可以尝试“键入”
#define
ala
#define S std::string(\"abc\")
,但是该常数避免了在每个使用点重复构造不同的临时对象 一个定义规则的并发症 可以获取地址,创建对它们的const引用等。 定义 \“全局\”范围/更容易出现用法冲突,这可能产生难以解决的编译问题和意外的运行时结果,而不是合理的错误消息;要缓解这一点,需要: 长的,晦涩的和/或集中协调的标识符,对它们的访问不能从隐式匹配使用的/当前/ Koenig查找的名称空间,名称空间别名等中受益。 通常需要使用所有大写字符并将其保留用于预处理程序定义(企业级预处理程序使用以保持可管理性的重要指南,以及可以期望遵循的第三方库),对其进行观察意味着将现有const或枚举迁移到定义涉及大写更改(因此影响客户代码)。 (就我个人而言,我大写枚举的第一个字母,但不大写consts,因此无论如何我都会被打到这里-也许是时候重新考虑一下。) 可能有更多的编译时操作:字符串文字串联,字符串化(取其大小) 缺点是给定
#define X \"x\"
和某些客户端用法ala
\"pre\" X \"post\"
,如果您希望或需要使X为运行时可更改的变量而不是常量,则会遇到麻烦,而鉴于它们已经强制执行,因此从
const char*
const std::string
的转换更容易用户合并串联操作。 不能直接在已定义的数字常量上使用sizeof 未输入(与未签名相比,GCC不会发出警告) 一些编译器/链接器/调试器链可能不提供标识符,因此您将被简化为查看“魔术数字”(字符串,无论如何...) 无法接受地址 在创建#define的上下文中,替换值不必是合法的(或离散的),因为在每个使用点都会对其进行评估,因此您可以引用尚未声明的对象,具体取决于\“ implementation \” ”(不需要预先添加),创建\“ constant \”,例如可用于初始化数组的
{ 1, 2 }
#define MICROSECONDS *1E-6
等(肯定不建议这样做!) 可以将一些特殊的东西(例如
__FILE__
和ѭ32be)合并到宏替换中 枚举 仅适用于整数值 正确确定范围/标识符冲突问题得到了很好的处理 强类型,但具有您无法控制的足够大的有符号或无符号整数大小(在C ++ 03中) 无法接受地址 较强的使用限制(例如递增-
template <typename T> void f(T t) { cout << ++t; }
不会编译) 每个常量的类型都从封闭的枚举中获取,因此,from34ѭ从不同的枚举中传递相同的数值时会得到不同的实例化,所有这些均不同于任何实际的f(int)实例化。 即使使用typeof,也不能期望numeric_limits提供有用的见解 枚举的类型名称可能出现在RTTI,编译器消息等的各个位置。-可能有用,可能会造成混淆 通常,我使用const并将它们视为一般用法中最专业的选择(尽管其他方法对老的懒惰程序员很有吸引力)。     
        宏不遵守范围,并且宏的名称对于符号调试器可能不可用。 Dan Saks关于宏(无),常量对象和枚举常量的相对优点的文章相当完整。与Stephen Dewhurst一样,Saks对于整数值更喜欢枚举常量,因为它们不占用存储空间(更确切地说,枚举常量既没有存储持续时间也没有链接)。     
        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;
}
    
        宏始终具有类型,例如,
#define FIVE 5
是int类型。 const变量相对于宏的一个优势可能是内存使用:使用宏,可能必须在使用该值的任何地方复制该值,而const变量将不会在内存中复制。 (但我不确定这种区别)     

要回复问题请先登录注册