天天看點

深入下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