例如,使用常量生成的代码比#define小 在他的书“有效C++第三版”中,Scott Meyer写了下面两行代码 #define ASPECT_RATIO 1.653

例如,使用常量生成的代码比#define小 在他的书“有效C++第三版”中,Scott Meyer写了下面两行代码 #define ASPECT_RATIO 1.653,c++,constants,C++,Constants,及 并解释 “…使用常量可能会产生比使用#define更小的代码。这是因为预处理器将宏名称纵横比盲替换为1.653可能会在目标代码中产生多个1.653的副本,而使用常量AspectRatio绝不会产生多个副本。” 对于当前的编译器,这仍然是正确的吗?我对常量的多次使用做了一些尝试,但在当前的g++中,这两个变量的大小是相同的。也许有人能给我举个有效的例子 谢谢 …而使用常量AspectRatio不应产生多个副本 不止一个副本取决于许多事情,特别是处理器指令和编译器优化设置。在优化速度时,多个拷贝

并解释

“…使用常量可能会产生比使用#define更小的代码。这是因为预处理器将宏名称纵横比盲替换为1.653可能会在目标代码中产生多个1.653的副本,而使用常量AspectRatio绝不会产生多个副本。”

对于当前的编译器,这仍然是正确的吗?我对常量的多次使用做了一些尝试,但在当前的g++中,这两个变量的大小是相同的。也许有人能给我举个有效的例子

谢谢

…而使用常量AspectRatio不应产生多个副本

不止一个副本取决于许多事情,特别是处理器指令和编译器优化设置。在优化速度时,多个拷贝可能会导致更快的执行。这种笼统的陈述不应该被提出,也不能被证明是合理的或支持的

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

#include <iostream>
#define THREE (3)
const int FOUR = 4;
int main()
{
    int value = THREE;
    std::cout << "Value is: " << value << "\n";
    return 0;
}
编译器创建符号“四”并将其放入一个关联值为4的符号表中。(可能还有其他与符号相关的属性,但为了便于说明,让我们保持简单)

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

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

实施 针对这两种情况发出的处理器指令取决于处理器和编译器的优化级别(可能还有编译器的复杂性)

即时模式
处理器具有访问或获取模式。为简单起见,我们关注即时或直接访问模式和间接访问模式。即时模式是指指令中包含值字段的模式。让我们称之为
MOVE
(如将常数移动到寄存器中):

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

在这种情况下,编译器会将数字插入指令的值字段。没有额外的空间消耗,没有额外的指令发出

间接模式
在间接模式下,处理器通过指针(地址)加载寄存器。处理器执行一个附加步骤,取消对指针的引用以获取值

+--------------------------------------+    +---------+  
|         LOAD operation/instruction   |    | Pointer |  
+--------------------+-----------------+    |    to   |  
+ Instruction Number | Register Number |    |  Value  |  
+--------------------+-----------------+    +---------+  
直接与间接的对比
某些处理器的立即值范围有限(例如8位),任何较大的值(例如
int
double
)都需要间接访问(指针/地址的附加字)。在惰性模式下,编译器可以简化操作并始终使用间接模式;立即模式将用于更高的优化级别

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

总结 使用
#define
宏或
const
变量的性能和大小取决于编译器的功能、优化级别和处理器指令。关于宏在空间或执行速度方面比常量变量更好或更差的笼统说法是站不住脚的。编译器和处理器的依赖项太多


常见的编码准则建议使用常量变量,因为它们有一个类型,可以防止基于不匹配类型的缺陷(编译器可以发出警告或错误)。

我认为今天的编译器可以优化这两个变量以获得相同的代码。也就是说,对于C++17及更高版本,
inline constexpr auto AspectRatio=1.653
是我定义编译时常量的首选方法。你甚至可以用这种技术在头文件中定义它们。
double
可能不是最好的例子,
const char*
似乎更相关。我个人认为
AspectRatio
的使用将被literal
1.653
取代,以提高性能。这似乎是一个奇怪的说法。该语句是不正确的,因为它很大程度上依赖于编译器和优化。例如,对于某些处理器,常量可以直接放入可执行文件中。对于其他处理器,常数可能被放入数据区域并从数据区域加载。对于后者,命名常量可能会节省更多代码。还取决于数值的类型和数值的大小。例如,ARM处理器可以用一条指令直接加载较小的值,而较大且唯一的值则存储在数据区域中。对于某些处理器,当加载到寄存器中时,该指令已经为数值内置了一个字段(也称为立即模式)。在这种情况下,将没有储蓄;该值被插入到可执行代码中。在ARM处理器的情况下,一些值被存储到一个数据区域,然后被间接获取(加载数据的地址,然后取消对地址/指针的引用)。有些编译器可能是惰性的(最低优化级别),并且可能对所有数值使用间接寻址(简化编译器构造)。应该注意的是,从技术上讲,它看起来像
int-value
  const int FOUR = 4;
value = FOUR;
+--------------------------------------+    +-------+  
|         LOAD operation/instruction   |    |       |  
+--------------------+-----------------+    |       |  
+ Instruction Number | Register Number |    | Value |  
+--------------------+-----------------+    +-------+  
+--------------------------------------+    +---------+  
|         LOAD operation/instruction   |    | Pointer |  
+--------------------+-----------------+    |    to   |  
+ Instruction Number | Register Number |    |  Value  |  
+--------------------+-----------------+    +---------+