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