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
rb_define_class函數定義了一個類test,rb_define_method将t_test方法以test的名稱添加到test類。在
t_test中,通過rb_str_new2每次生成一個rstring結構,然後通過rb_str_cat2将str與"ing"連接配接起來,添加
了一些列印用于跟蹤。利用mkmf産生makefile,寫一個extconf.rb
require 'mkmf'
create_makefile("string_hack");
執行ruby extconf.rb,将産生一個makefile,執行make,生成一個string_hack.so的連結庫。擴充寫完了,通過
ruby調用:
require 'string_hack"
t=test.new
(1..3).each{|i| t.test}
輸出:
before concat: str:0x40098a40, str.aux.shared:0x3, str.ptr:str
after concat: str:0x40098a40, str.aux.shared:0x8, str.ptr:string
before concat: str:0x40098a2c, str.aux.shared:0x3, str.ptr:str
after concat: str:0x40098a2c, str.aux.shared:0x8, str.ptr:string
before concat: str:0x40098a18, str.aux.shared:0x3, str.ptr:str
after concat: str:0x40098a18, str.aux.shared:0x8, str.ptr:string
從結果可以看出,在str concat之前之後,str指向的位置沒有改變,改變的僅僅是str中ptr指向的字元串的值
,看看rb_str_cat2函數的實作就一目了然了:
value rb_str_cat(str, ptr, len)
value str;
const char *ptr;
if (len < 0) {
rb_raise(rb_eargerror, "negative string size (or size too big)");
}
if (fl_test(str, str_assoc)) {
rb_str_modify(str);
realloc_n(rstring(str)->ptr, char, rstring(str)->len+len);
memcpy(rstring(str)->ptr + rstring(str)->len, ptr, len);
rstring(str)->len += len;
rstring(str)->ptr[rstring(str)->len] = '"0'; /* sentinel */
return str;
return rb_str_buf_cat(str, ptr, len);
value rb_str_cat2(str, ptr)
return rb_str_cat(str, ptr, strlen(ptr));
//string.c
文章轉自莊周夢蝶 ,原文釋出時間2007-09-12