在perl中只有3種基本的數據結構:標量、數組、hash。變量能夠是數值,能夠是字符串。正則表達式
這三種基本數據結構的數據存儲方式以下:算法
可是,僅僅由這3種基本結構,就能夠構造出更復雜的數據結構,例如hash中用數組作value,數組中用hash作元素。數組
可是,perl對於底層的一些數據存儲,不少時候對這些數據是直接拷貝存儲的。而有些時候是不必去拷貝數據的,經過引用,能夠避免拷貝操做,哪裏須要數據,用數據對象的引用便可,也就是插一個"指針"的事。數據結構
引用就像是指針,對一個引用進行輸出,在字符串上下文將返回它指向的目標數據的類型和內存地址,在數值上下文將返回地址空間。例如,數組的引用和hash的引用,以下圖。函數
上圖中,將數組@name
的引用賦值給了一個名爲$arr_ref
的標量,將hash@member
的引用賦值給了一個名爲$hash_ref
的標量。注意,引用都是標量。ui
引用的方式很簡單,只需在 數組的@
符號、hash的%
符號以前加上反斜線\
。例如\@name
就表示這是名稱爲name的數組的引用,\%member
就表示這是名稱爲member的hash的引用。操作系統
例如:scala
@name=qw(longshuai xiaofang wugui tuner); %member=( longshuai => "18012345678", xiaofang => "17012345678", wugui => "16012345678", tuner => "15012345678" ); $arr_ref=\@name; # 將數組的引用賦值給一個標量 $hash_ref=\%member; # 將hash的引用賦值給一個標量 print "$arr_ref","\n"; # 輸出:ARRAY(0x18a5fa0) print "$hash_ref","\n"; # 輸出:HASH(0x18a6060)
因爲引用被看成一個標量,因此能使用標量的地方,就能使用引用。例如,將一個數組的引用放進hash中:3d
@name=qw(longshuai xiaofang wugui tuner); $arr_ref=\@name; %member=( longshuai => "18012345678", xiaofang => "17012345678", wugui => "16012345678", tuner => $arr_ref ); print "$member{'tuner'}","\n"; # 輸出數組的引用:ARRAY(0x13d7fa0)
除數組、hash有引用,標量、函數也有引用,並且還不止這些,好比文件句柄引用、正則表達式引用等。指針
目前,暫時只需瞭解標量、數組、hash、函數的引用方式便可,分別爲:
\$x # 引用 scalar \@y # 引用 array \%z # 引用 hash \&f # 引用 function
下面建立一個指向$foo1
值的引用$ref_foo1
,也就是說$ref_foo1
和foo1是等價的,要引用這個變量的值,須要使用$$ref_foo1
或$foo1
。
my $ref_foo1 = \$foo1;
例如:
my $foo1="xyz"; my $ref_foo1 = \$foo1; print "$$ref_foo1\n";
常常會看到下面一種代碼格式:
my $ref_foo2 = \my $foo2;
等號右邊\my $foo2
表示先建立一個用my修飾的變量$foo2
(由於未賦值,因此初始化爲undef),再建立一個指向它的引用$ref_foo2
。和上面的示例相比,它只是多了一個臨時建立變量的功能,由於my的優先級高於\
,它等價於\(my $foo2)
。
上面的語句還能夠賦值:
my $ref_foo2 = \(my $foo2="abcd"); print "$$ref_foo2\n";
能夠爲同一段數據對象建立多個引用,因爲每一個引用都指向同一段數據對象,因此相同數據對象的引用是徹底一致的。
因爲引用在數值上下文返回引用對象的地址空間,因此能夠用比較操做符==
來比較引用,固然,使用eq
來比較也徹底能夠:
@name=qw(longshuai xiaofang wugui tuner); $arr_ref=\@name; $arr_ref1=\@name; $arr_ref2=$arr_ref; print "ref equals\n" if $arr_ref == \@name; # true print "ref equal ref1\n" if $arr_ref == $arr_ref1; # true print "ref1 equal ref2\n" if $arr_ref1 == $arr_ref2; # true
在perl中,對每段數據的引用都會維護一個引用計數器:
這和硬連接相似,每建立一個硬連接,硬連接數量就加1,每刪除一個硬連接,硬連接數量就減1。
#!/usr/bin/perl use 5.010; @names=qw(longshuai xiaofang wugui tuner); # ref counter = 1 $arr_ref1=\@names; # ref counter = 2 $arr_ref2=$arr_ref1; # ref counter = 3 $arr_ref3=\@names; # ref counter = 4 $arr_ref2=undef; # ref counter = 3 @names=undef; # ref counter = 2 say "arr: @names"; # 輸出:arr: say "ref1: $arr_ref1"; # 輸出:ARRAY(0x55befad8f0a0) say "ref2: $arr_ref2"; # 輸出:ref2: say "ref3: $arr_ref3"; # 輸出:ARRAY(0x55befad8f0a0)
若是使用my或local這樣做用域關鍵詞去定義引用,當引用超出對應邊界後,就會失效,引用計數器就會對應減1。固然,若是不定義爲my或local,則超出邊界也不會失效。
#!/usr/bin/perl use 5.010; @names=qw(longshuai xiaofang wugui tuner); # ref counter = 1 $arr_ref1=\@names; # ref counter = 2 { my $arr_ref2=$arr_ref1; # ref counter = 3 my $arr_ref3=\@names; # ref counter = 4 } # ref counter = 2 say "ref1: $arr_ref1"; say "ref2: $arr_ref2"; # arr_ref2已經失效 say "arr: @names"; say "ref3: $arr_ref3"; # arr_ref3已經失效
有一點須要注意,就是將引用做爲子程序(函數)的參數時,它首先將引用賦值給@_
特殊變量,這時引用計數會加1,當退出子程序時,@_
將失效,引用計數器將減1。
&mysub(\@names); # 直接將引用賦值給@_ &mysub($arr_ref); # 拷貝引用,@_也將引用數據對象
perl採用引用計數的方式回收內存空間。引用計數管理內存的方式已經存在了好久,和GC(Garbage collection)垃圾回收機制是同一年(1960年)被研究出來的。不管是哪一種內存管理機制,都有優勢,也都有缺點。
對於引用計數管理方式而言,它最大的優勢在於:
使用引用計數管理內存最大的缺點在於:
相比於引用計數管理內存的方式,GC回收機制都是在內存不夠後,遍歷整個內存來回收的,因此有延遲性和低效性(有好幾種改進的GC,都各有優缺點,但原始的GC算法就是如此)。
其實不管是引用計數仍是GC,都對它們各自的缺點有各類改進,但修補缺陷的同時,也會損傷它們的優勢,因此不一樣語言針對不一樣適用場景,老是採用不一樣的折衷手段。
回到引用計數的一個缺點問題上:沒法回收循環引用問題。所謂循環引用,是指A引用B的對象,B又引用A的對象。例如:
@name1=qw(longshuai wugui); @name2=qw(xiaofang tuner); push @name1,\@name2; push @name2,\@name1;
驗證下:
#!/usr/bin/perl use 5.010; @name1=qw(longshuai wugui); @name2=qw(xiaofang tuner); say "name1_1: ",\@name1; # ARRAY(0xNAME1) say "name2_1: ",\@name2; # ARRAY(0xNAME2) push @name1,\@name2; push @name2,\@name1; say "name1_2: @name1"; # ARRAY(0xNAME2) say "name2_2: @name2"; # ARRAY(0xNAME1)
以下圖所示:
這樣的引用環路,使得@name1
和@name2
對應的數據對象的引用,不管如何也沒法減小到0。
這樣會致使一個重大問題:內存泄漏(memory leak)。隨着泄漏愈來愈多,內存終有被耗盡的時刻。也許真正寫成內存泄漏的代碼比較少,但不少時候構建複雜的數據結構時,無心中可能就會出現引用環路問題。
要避免引用環路,可使用perl的另外一種引用行爲:弱引用(weak)。或者,在退出引用做用域以前,打破引用環路。例如:
{ @name1=qw(longshuai wugui); @name2=qw(xiaofang tuner); push @name1,\@name2; push @name2,\@name1; ...... # 退出做用域以前,在引用的內部清空引用數據 @name1=(); @name2=(); }
這樣,在退出做用域後,@name1=qw(longshuai wugui (這段空))
,同理@name2
。如此一來,@name1
和@name2
又變回了最初狀態,都只有一個引用,且沒有了循環引用。
固然,上面是直接清空初始引用的,若是push進去的是引用變量,則清空引用變量便可。
{ @name1=qw(longshuai wugui); @name2=qw(xiaofang tuner); $name1_ref=\@name1; $name2_ref=\@name2; push @name1,$name1; # 使用引用變量,而非初始的數組名引用 push @name2,$name2; ...... # 退出做用域以前,在引用的內部清空引用數據 $name1=undef; $name2=undef; }
要檢查是否出現內存泄漏問題,可使用Test::Memory::Cycle
模塊。
最後,perl目前尚未其它垃圾回收機制(garbage collection),也許在將來的版本中可能會引入新的gc管理方式來替代引用計數的管理方式。