Project1

标题: Ruby跟C语言交互的时候,只使用单数,那不是很浪费么。 [打印本页]

作者: 尘羽泯    时间: 2011-8-20 17:06
标题: Ruby跟C语言交互的时候,只使用单数,那不是很浪费么。
本帖最后由 尘羽泯 于 2011-8-20 17:13 编辑
  1. /* special contants - i.e. non-zero and non-fixnum constants */
  2. #define Qfalse ((VALUE)0)
  3. #define Qtrue  ((VALUE)2)
  4. #define Qnil   ((VALUE)4)
  5. #define Qundef ((VALUE)6)        /* undefined value for placeholder */
复制代码
以上是ruby.h里面定义的四个常量。
于是我就生了问题,当传递Integer的时候,如果恰好传2,那么不是会变成True么?可是Ruby里面2==true显然不可能成立。
下面我用rb_define_global_function定义了一个函数,
VALUE a(VALUE b){
MessageBox(char(b),0,"")
return b
};

在C语言与Ruby交互的时候终于找到了规律
下面是Ruby代码:
print a 0 #=>C的MessageBox显示1,Ruby的print显示0
print a 1 #=>C的MessageBox显示3,Ruby的print显示1
print a 2#=>C的MessageBox显示5,Ruby的print显示2
print a 3#=>C的MessageBox显示7,Ruby的print显示3
print a 4#=>C的MessageBox显示9,Ruby的print显示4
print a 5#=>C的MessageBox显示11,Ruby的print显示5
于是我知道了。Ruby里面的Integer变量,到了C里面,都会变成单数,双数用来定义false、true、nil等特殊的类型,这样就可以区别nil、false、true和普通的Integer。

不过,这样子的话。32位的有符号整数,去掉了一半用来存这仅仅4个类型,Ruby这样做,是不是很失败呢。那不是很浪费么?而且。取值范围也减少了一半。


尘羽泯于2011-8-20 17:33补充以下内容:
哦哦,我知道了,Ruby里面的String、Array等类型,传递到C里面,也是一个双数的指针,这样就可以区别变量类型了。单数是Integer,把(它-1)/2就可以得到Ruby里面传过来的值,双数的话0、2、4、6分别代表特定的4种类型,再大一点就是内存指针,可能是Strng或Array什么的。
作者: fux2    时间: 2011-8-20 18:03
我想知道你知道单数是什么意思吗?
作者: yangff    时间: 2011-8-20 18:06
= =这是 Ruby的储存方式把
作者: DeathKing    时间: 2011-8-20 19:14
Fixnum 类表示小整数,由 INT2FIX 得到。由于可能在程序中频繁使用,将其用结构体表示可能会使执行速度大打折扣,因此直接嵌入 VALUE 中。数值左移一位或上0x01使得 Fixnum总是一个奇数,其实际可用比特位为 sizeof(long)*8-1 ,可表示的最大最小值分别为FIXNUM_MAX 和 FIXNUM_MIN ,超出该范围的数属于 Bignum 类,由C结构体实现。


http://www.ibm.com/developerworks/cn/opensource/os-cn-rubysbl2/

作者: 苏小脉    时间: 2011-8-20 23:53
本帖最后由 苏小脉 于 2011-8-21 00:04 编辑
Ruby里面的String、Array等类型,传递到C里面,也是一个双数的指针,这样就可以区别变量类型了

不仅是“双数”而已,在当代的 32 位机下,指针的存储永远是在一个长度为 4 字节以上的界限内 [1],如此一来指针的两个最低的有效位可以保证为 0,也就是整个指针存储的数据永远是 4 的倍数。CRuby 实现就依赖了这个事实,当这两个位有一个位为 1 时,当前的 VALUE 就是直接值,而不是对象指针。

Ruby里面的Integer变量,到了C里面,都会变成单数,双数用来定义false、true、nil等特殊的类型,这样就可以区别nil、false、true和普通的Integer。

“单数”、“双数”这样的说法容易引起歧义,不够严谨。CRuby 目前是这样实现的:

VALUE 最低有效位如果为 1,则为 Fixnum,Fixnum 的值存储在其余 31 位中,故右移一位可得。

0 = 0b0000,2 = 0b0010,4 = 0b0100,6 = 0b0110
这里面 2 和 6 由于第二最低有效位为 1,所以肯定是直接值;0 和 4 虽然刚好在 4 的界限上,但在当代的 OS 下属于 OS 保留字段,不可能成为虚拟地址,故而这些都可以用来存储直接值。

Symbol 也是直接值,但却是用的最低有效字节(8 个位)来标识。如果最低有效字节 = 0x0e = 0b00001110,那么最高 24 位就是 Symbol 的 ID,故通过左移、右移 8 位可互换。

不过,这样子的话。32位的有符号整数,去掉了一半用来存这仅仅4个类型,Ruby这样做,是不是很失败呢。

目前 CPython 实现也是采取的这个路子。若果不这么做,直接值就须得动态分配或是使用 tagged 结构联合。通常动态内存分配的效率自然是不及静态内存拷贝的,在大部分 C 实现下,前者要经过堆的统一管理,而后者只是简单的栈操作;tagged 结构联合虽然不需要动态分配,但每次都依靠函数传值,这个拷贝过程也是昂贵的,还别提 tagged 联合的内存泄漏问题最容易被忽视。实际上目前这种 VALUE 存储方式就是 tagged 联合的一种,只不过巧妙地避免了不必要的开销。

而且。取值范围也减少了一半。

当需要和大于 2^30-1 的数打交道时,各种运算的操作只怕轻易就超过 32 位整数范围了,横竖动态语言支持 Bignum,怕他作甚。

[1] 64 位编译环境下界限为  8 字节。

作者: 尘羽泯    时间: 2011-8-21 06:26
本帖最后由 尘羽泯 于 2011-8-21 09:26 编辑
苏小脉 发表于 2011-8-20 23:53
不仅是“双数”而已,在当代的 32 位机下,指针的存储永远是在一个长度为 4 字节以上的界限内 [1],如此一 ...


膜拜紫苏帝,见到紫苏帝,我的眼泪就止不住汪汪地掉。


VALUE 最低有效位如果为 1,则为 Fixnum,Fixnum 的值存储在其余 31 位中,故右移一位可得。

看到这里,我终于知道了我有多傻
(伪代码:)
Integer Fixnum2Integer(VALUE fix){
___asm("
mov eax,[ebp+8]
dec eax
shr eax,1
leave
ret 4
")}
右移之后,最低位本来就没了,我竟然傻乎乎的在前面dec - -b 啊啊啊。

Symbol 也是直接值,但却是用的最低有效字节(8 个位)来标识。

Ruby都忘得差不多啦,Symbol是啥玩意儿都给忘了。(好像原来也不曾知道过,貌似是符号- -b)
  1. To convert C data to Ruby values:

  2. FIXNUM  left shift 1 bit, and turn on LSB.  
复制代码
这个是网上看到的一段注释,不过不懂turn on LSB是啥。
----------
感谢紫苏的热情解答(注释也很可爱 - -b)。不过。我发现,紫苏是神啊,从不曾睡觉(从23点,到5点,这段时间,一直有发帖呢)。




欢迎光临 Project1 (https://rpg.blue/) Powered by Discuz! X3.1