オブジェクトID

先日の日記で、Fixnum のオブジェクトのオブジェクトIDと String のオブジェクトのオブジェクトIDを調べた。そのとき気になったことがあったので、少し調べてみた。気になったことというのは、Fixnum と String のオブジェクトIDがあまりにも違いすぎる、ということ。そのときの値は、Fixnum が 3 で String が 84070 だった。
理由は Rubyソースコード完全解説 第2章 オブジェクトで発見。基本はオブジェクトの実態へのポインタを VALUE 型の変数に入れておくのだが、Fixnum の場合は VALUE 型の変数に入っているのはポインタではなく、実際の値が入っている。で、オブジェクトIDはこのVALUEの値を表示しているみたい。
オブジェクトIDをどのようにして求めているかというと、rb_obj_id() という関数を使っていて、その中身はこうなっていた。

VALUE
rb_obj_id(VALUE obj) 
{
    /* 中略 */

    if (TYPE(obj) == T_SYMBOL) {
        return (SYM2ID(obj) * sizeof(RVALUE) + (4 << 2)) | FIXNUM_FLAG;
    }    
    if (SPECIAL_CONST_P(obj)) {
        return LONG2NUM((long)obj);
    }    
    return (VALUE)((long)obj|FIXNUM_FLAG);
}
(gc.c)

シンボルの場合と、特別な値(nil, true, false, Fixnum)の場合と、それ以外の場合の3通りあるようだ。String のオブジェクトはそれ以外の場合になる。ん...それ以外の場合は obj と FIXNUM_FLAG を OR 演算してる。FIXNUM_FLAG は 1 だから OR したら LSB は必ず 1 になるよね。でも、実際に表示させてみたら 84070 と表示されたぞ。〜見るところ間違ったかな。rb_obj_id() 内のコメントを見てもよくわからん。