如何解决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变量将不会在内存中复制。 (但我不确定这种区别)
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。