Ruby 关于编码的一些问题

maxchen · 2018年10月24日 · 最后由 luikore 回复于 2018年10月24日 · 1202 次阅读

Ruby 的开发文档中,对象的默认 inspect 方法里写到:

obj.inspect → string

Returns a string containing a human-readable representation of obj. The default inspect shows the object's class name, an encoding of the object id.

这里的 an encoding of the object id 是怎么实现的呢?也就是"#"中的这一串东西“0x0000000003630558”是怎么来的呢?

Rei 回复

这个地址和 obj.object_id 有什么区别呢?因为我发现这不一样。

这个问题我之前也没留意,搜了一下,以前有讨论帖

https://ruby-china.org/topics/15581

还有 StackOverflow 的回答

https://stackoverflow.com/questions/3430280/how-does-object-id-assignment-work https://stackoverflow.com/questions/2818602/in-ruby-why-does-inspect-print-out-some-kind-of-object-id-which-is-different/2818916#2818916

大概意思是 Ruby 计算不同的对象的 obejct_id 时候用了不同的规则,让一些不需要分配内存的值对象 id 和其它 obejct 不冲突,除了 true/false/nil 等固定值外,比较特殊的有 fixnum 和 obejct 的计算规则。

fixnum object_id = 值 * 2 + 1

object object_id = 指针地址 / 2

因为在某些计算机上,指针地址的末位都是 0,所以就把末位为 1 的空间除了几个固定值以外的空间都分配给 fixnum。(写到这里我觉得 object 的 object_id 不除以 2 也可以啊,也许是为了显得紧凑?期待其它人回答)

反过来说,一般对象的 object_id 乘以 2 就是指针地址:

> user.to_s
=> "#<User:0x00000008b8bca0>"
> user.object_id
=> 73162320
> (user.object_id * 2).to_s 16 # 16 进制显示
=> "8b8bca0"
> (user.object_id << 1).to_s 16 # 二进制里面,乘以 2 其实就是左移 1 位。
=> "8b8bca0"
> 0x00000008b8bca0.to_s 2 # 2 进制显示
=> "1000101110001011110010100000"
>  user.object_id.to_s 2 # 二进制显示,比上面末尾少一个 0,
=> "100010111000101111001010000"
> (user.object_id << 1).to_s 2 # 二进制比较容易看出为什么末尾为 1 就能区分 fixnum 和 object。
=> "1000101110001011110010100000"
Rei 回复

👏 终于解决了这个困惑了,谢谢@Rei

maxchen 关闭了讨论。 10月24日 16:41
maxchen 重新开启了讨论。 10月24日 16:41
Rei 回复

除以 2 是因为想要 object_id 这个方法的返回结果保持 Fixnum 类型

需要 登录 后方可回复, 如果你还没有账号请 注册新账号