ruby语言中的string是mutable的,不像java、c#中的string是immutable的。比如
str1="abc"
str2="abc"
在java中,对于字面量的字符串,jvm内部维持一张表,因此如果在java中,str1和str2是同一个string对象。而在ruby中,str1和str2是完全不同的对象。同样,在java中对于string对象的操作都将产生一个新的对象,而ruby则是操纵同一个对象,比如:
str="abc"
str.concat("cdf")
struct rstring {
struct rbasic basic;
long len;
char *ptr;
union {
long capa;
value shared;
} aux;
};
//ruby.h
显然,len是string的长度;ptr是一个char类型的指针,指向实际的字符串;然后是一个联合,这个稍后再说。如果你看看ruby.h可以发现,几乎所有定义的对象结构都有一个struct rbasic。显然,struct rbasic包含由所有对象结构体共享的一些重要信息的。看看rbasic:
struct rbasic {
unsigned long flags;
value klass;
其中的flags是一个多用途的标记,大多数情况下用于记录结构体的类型,ruby.h中预定义了一些列的宏,比如t_string(表示struct rstring),t_array(表示struct rarray)等。klass是一个value类型,value也是unsigned long,可以地将它当成指针(一个指针4字节,绰绰有余了),它指向的是一个ruby对象,这里以后再深入。
那么联合aux中的capa和shared是干什么用的呢?因为ruby的string是可变的,可变意味着len可以改变,我们需要每次都根据len的变换来增减内存(使用c中的realloc()函数),这显然是一个很大的开销,解决办法就是预留一定的空间,ptr指向的内存大小略大于len,这样就不需要频繁调用realloc了,aux.capa就是一个长度,包含额外的内存大小。那么aux.shared是干什么的呢?这是一个value类型,说明它是指向某个对象。aux.shared其实是用于加快字符串的创建速度,在一个循环中:
#include<stdio.h>
static value t_test(value self)
{
value str;
str=rb_str_new2("str");
printf("before concat: str:%p, str.aux.shared:%p, str.ptr:%s"n",str,
(rstring(str)->aux).shared,rstring(str)->ptr);
rb_str_cat2(str,"ing");
printf("after concat: str:%p, str.aux.shared:%p, str.ptr:%s"n",
str,(rstring(str)->aux).shared,rstring(str)->ptr);
return self;
}
value ctest;
void init_string_hack(){
ctest=rb_define_class("test",rb_cobject);
rb_define_method(ctest,"test",t_test,0);
//string_hack.c