使用常量产生的代码比#define

时间:2019-05-31 12:23:19

标签: c++ const

在他的《有效的C ++第三版》一书中。 Scott Meyer编写了以下两行代码

#define ASPECT_RATIO 1.653

const double AspectRatio = 1.653;

并解释

“ ......使用常数可能比使用#define产生的代码更小。这是因为预处理器用1.653盲目替换宏名称ASPECT_RATIO可能导致目标代码中有1.653的多个副本。使用常量AspectRatio绝不能导致一个以上的副本。”

这对于当前的编译器仍然适用吗?我使用了常量的多种用法,但在使用当前g ++的两个变量中都得到了相同的大小。也许有人可以给我看一个可行的例子?

谢谢。

1 个答案:

答案 0 :(得分:2)

  

...尽管使用常量AspectRatio绝不应该导致一个以上的副本

一个以上的副本取决于很多事情,尤其是处理器指令和编译器优化设置。在优化速度时,一个以上的副本可能会导致更快的执行速度。不能证明或支持这种这样的笼统声明。

预处理

#define宏的内容由编译的预处理阶段处理。宏的内容会在编译(翻译)开始之前插入。举一个简单的例子:

#include <iostream>
#define THREE (3)
const int FOUR = 4;
int main()
{
    int value = THREE;
    std::cout << "Value is: " << value << "\n";
    return 0;
}

预处理后,编译器将看到:

// contents of iostream header
const int FOUR = 4;
int main()
{
    int value = 3;
    std::cout << "Value is: " << value << "\n";
    return 0;
}

#define宏与直接将数字粘贴到代码中没什么不同。

由于将数字直接粘贴到代码中,因此编译器会推导常量的类型(有符号与无符号,整数与浮点),然后发出代码以将数字分配给变量。

标识符/符号

当编译器遇到以下语句时:

  const int FOUR = 4;

编译器创建符号“ FOUR”,并将其放入关联值为4的 symbol 表中。(该符号可能还有其他属性,但是为了说明起见,我们将其简化一下)。

当编译器遇到如下语句时:

value = FOUR;

编译器遇到符号“ FOUR”,在 symbol 表中查找,检索值并继续进行处理,类似于处理语句value = 4;

实施

在两种情况下发出的处理器指令都取决于处理器和编译器的优化级别(可能还取决于编译器的复杂性)。

立即模式
处理器具有访问或获取模式。为简单起见,我们关注立即直接访问模式和间接模式。 立即模式是指令中包含该值的字段的模式。我们称它为MOVE(就像将常量移到寄存器中一样):

+--------------------------------------+    +-------+  
|         LOAD operation/instruction   |    |       |  
+--------------------+-----------------+    |       |  
+ Instruction Number | Register Number |    | Value |  
+--------------------+-----------------+    +-------+  

MOVE指令包含两个字段:指令代码和要加载到寄存器中的值。 MOVE指令始终有两个字段。 注意:值字段可以合并到指令单位(字)中。

在这种情况下,编译器会将数字插入到指令的value字段中。无需占用额外的空间,也不会发出额外的指令。

间接模式
在间接模式下,处理器通过指针(地址)加载寄存器。处理器采取了附加步骤,即对指针进行解引用以获取值。

+--------------------------------------+    +---------+  
|         LOAD operation/instruction   |    | Pointer |  
+--------------------+-----------------+    |    to   |  
+ Instruction Number | Register Number |    |  Value  |  
+--------------------+-----------------+    +---------+  

立即与间接
一些处理器的立即值范围可能有限(例如8位),任何更大的值(例如intdouble)都将需要间接访问(指针/地址的附加字) )。处于懒惰模式的编译器可以简化操作,并始终使用间接模式。立即模式可用于更高的优化级别。

在优化空间时,编译器可以通过对公共常数(例如PI)使用间接模式来节省空间。使用常量变量(而不是宏)将使此任务更加容易。但是,编译器无论如何也可以使用该值(当遇到3.14159 ...时,可以将其存储在表中以备后用)。

摘要

使用#define宏或const变量的性能和大小取决于编译器的功能,优化级别和处理器指令。笼统地说,对于空间或执行速度而言,宏比常数变量好还是差,这是没有道理的。编译器和处理器的依赖性过多。

通用编码指南建议使用常量变量,因为它们具有类型并且可以防止基于不匹配类型的缺陷(编译器可以发出警告或错误)。