天天看点

深入下Ruby中的String

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