注意,以下所有内容都是针对默认的Ruby,内部使用YARV又名"又一个Ruby VM",其他像JRuby这样的红宝石可能会使用不同的内部表示......
好问题.
Ruby使用整数的标记指针,其他所有内容都存储为对象的引用.
他们是如何工作的?指针中的一位用作标记,如果该位置位,则指针的其余部分被解释为整数,否则被解释为地址.
这是有效的,因为没有使用指针中的某些位.通常不使用存储器地址的最低位.大多数系统仅允许对齐的存储器地址的地址,例如对齐到4个字节,因此2位可用作标记.然后,如果设置了该标记,则指针的另一个31位将被解释为整数.
当你查看object_id
整数时,你可以看到这一点
20.to_s(2) # => "10100"
20.object_id.to_s(2) # => "101001"
Run Code Online (Sandbox Code Playgroud)
在某些系统上使用两个标记位,然后使用另一个标记位表示浮点数.并且有一些像nil, true, false
这样的特殊对象用保留的数字表示,这些数字不太可能是有效的内存地址.符号也在内部表示为带标记的整数,但具有与实际整数不同的位掩码.
所有其他值都表示为指针.
有趣的是,您可以使用ObjectSpace
课程自己检查所有这些.
(0..100).each { |n| p([n, ObjectSpace._id2ref(n)]) rescue nil }
Run Code Online (Sandbox Code Playgroud)
在我的系统上打印
[0, false]
[1, 0]
[2, 2.0]
[3, 1]
[5, 2]
[6, -2.0]
[7, 3]
[8, nil]
[9, 4]
[10, 2.0000000000000004]
[11, 5]
[13, 6]
[14, -2.0000000000000004]
[15, 7]
[17, 8]
[18, 2.000000000000001]
[19, 9]
[20, true]
[21, 10]
[22, -2.000000000000001]
[23, 11]
...
Run Code Online (Sandbox Code Playgroud)
TL;博士:不要紧,你看不出来,因为你不能告诉,Ruby语言规范不说什么,它允许不同的实现者做出不同的选择,他们做的事实上做出不同的选择。
没关系。
您可以区分差异的唯一方法是修改对象,但由于所有直接对象都是不可变的,因此您无法区分一种方式或另一种方式。
事实证明,不同的 Ruby 实现以不同的方式对待它们,这并没有错。例如,YARV 将Integer
s存储为标记指针(称为fixnums)或对象引用(称为bignums),具体取决于大小,但同样,可以将完全相同的数字存储为两者之一,因为在 64 位系统上,它使用 63 位作为fixnums,而在 32 位系统上,它只使用 31 位。JRuby OTOH 不使用标记指针(它根本不使用指针,因为 Java 根本没有它们),并且无论机器字大小如何,都使用完整的 64 位作为fixnum,而不是使用 31 或 63 位的 YARV .
同样,64 位系统上的 YARV 对Float
s使用 62 位标记指针格式,适合 62 位(他们称之为flonums),但在 32 位系统和更大的Float
s 上,它使用不同的编码。