假设我有一个 C 程序,其中有这一行:
int a = 12;
12 的值在编译时是否绑定到 'a' ?或者是在运行时当程序范围达到“a”时将值放入内存中?
Python 和 Ruby 这样的编程语言怎么样?
是否存在值静态绑定到变量的语言/实例?我已经考虑这个问题有一段时间了,老实说,我想不出将值静态绑定到原始类型的逻辑原因。
编译器和虚拟机有效地“实现”编程语言。它们只需执行语言语义指定的操作以及给定程序可观察到的操作即可正确。
当您在 C 程序中编写定义语句
int a = 12;
时,您是在通知编译器有一个名为 a
的变量,其初始值设置为常量文字 12。
如果你从不观察
a
,编译器可以完全摆脱它,因为你无法根据语言规范区分程序执行期间的差异。 (例如,如果您要暂停程序并在此时扫描堆栈,则值 12 根本不需要存在。它位于机器堆栈上的必要性并不是语言规范的一部分。)
如果您观察 a
但发现您的程序不可能改变其值,则编译器已推断出它实际上是
static const
,并且可以执行称为“恒定传播”的优化,将硬编码的 12 推入相关说明。如果您引用
a
,如
int *aptr = &a;
,那么语言规范表示 a
必须在内存中具有一个位置 (IIRC)。这意味着,根据规范,内存中(在堆栈上,在任何合理的实现中)将有一个有效的、int 大小的槽,其中包含 12。 在其他语言中显然有其他规范。从技术上讲,在动态语言中,编译器可以执行类似的优化。如果你有Python函数:
def do_nothing():
a = 12
您可以想象,将语言文本转换为字节码的 Python 编译器可能会选择完全省略该函数的主体(对于学究来说:除了隐式的
return None
)。传统上,Python 实现没有此类优化,因为它的语言哲学要求可调试性、VM 实现简单性以及程序员对优化的理解。
动态语言中还存在一些棘手的结构,使推理变得更加困难。例如,Python 允许进行大量的代码对象检查,这些检查预计可以跨实现工作。获取/检查堆栈跟踪和激活帧的本地参数需要“至少”慢路径回退机制,这会显着增加优化复杂性。我们将在 ECMAScript 修订版 5 中放弃对 JavaScript 中某些语言级运行时堆栈检查的支持。
支持调试器很难,让我们开始优化吧!
值 12 并未静态绑定到
a
a
绑定到一个内存位置,该位置被初始化为值 12。
如果
a
被声明为 const
并且没有任何东西占用它的地址,编译器将不需要分配存储空间并可以将其用作编译时常量,在这种情况下,值 12 将静态绑定到
a
。这实际上取决于变量使用的上下文和您启用的编译器优化。如果变量从未在足够小的范围内更新和使用,则即使在最轻的优化中,它也可能在最终输出中被编译出来。如果编译器检测到变量被传递或稍后更新(或可能稍后更新),它可能会将整数加载到堆栈上。这取决于架构,但如果它只在短时间内更新然后被处理,它可能会将其加载到寄存器中(如果可以的话)并在那里使用它。
我不是一个完全的专家,因为我只编写了非常基本的编译器,这些编译器输出到字节码以便稍后进行 JIT 处理,那是几年前的事了。
我认为
a
a
不是常量,这意味着可能同时有多个
a
值(例如在递归函数中声明a
)。重要的是,a
的每个值必须在内存中具有单独的地址,这意味着a
不能静态绑定到特定的单个内存地址。