久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

Delphi 中String類型原理介紹

 fssky 2011-08-01

Delphi 中String類型原理介紹

      在網(wǎng)上看到一些介紹delphi中String類型的文章,受益菲淺,,確定將其一一摘錄,,放在blog中,認(rèn)真學(xué)習(xí),、思考,!

[原文]  
   Delphi中字符串的操作很簡單,但幕后情況卻相當(dāng)復(fù)雜,。Pascal傳統(tǒng)的字符串操作方法與Windows不同,,Windows吸取了C語言的字符串操作方法。32位Delphi中增加了長字符串類型,,該類型功能強(qiáng)大,,是Delphi缺省的字符串類型,。  

   字符串類型在Borland公司的TurboPascal和16位Delphi中,,傳統(tǒng)的字符串類型是一個字符序列,序列的頭部是一個長度字節(jié),,指示當(dāng)前字符串的長度,。由于只用一個字節(jié)來表示字符串的長度,所以字符串不能超過255個字符,。這一長度限制為字符串操作帶來不便,,因為每個字符串必須定長(確省最大值為255),當(dāng)然你也可以聲明更短的字符串以節(jié)約存儲空間,。  

   字符串類型與數(shù)組類型相似,。實際上一個字符串差不多就是一個字符類型的數(shù)組,因此用[]符號,你就能訪問字符串中的字符,,這一事實充分說明了上述觀點,。  
   為克服傳統(tǒng)Pascal字符串的局限性,32位Delphi增加了對長字符串的支持,。這樣共有三種字符串類型:  
   ShortString   短字符串類型也就是前面所述的傳統(tǒng)Pascal字符串類型,。這類字符串最多只能有255個字符,與16位Delphi中的字符串相同,。短字符串中的每個字符都屬于
                        ANSIChar類型(標(biāo)準(zhǔn)字符類型),。    
   ANSIString  長字符串類型就是新增的可變長字符串類型。這類字符串由內(nèi)存動態(tài)分配,,引用計數(shù),,并使用了更新前拷貝(copy--on-write)技術(shù)。這類字符串長度沒有限制(可        以存儲多達(dá)20億個字符?。?,其字符類型也是ANSIChar類型。    
   WideString   長字符串類型與ANSIString 類型相似,,只是它基于WideChar字符類型,,WideChar字符為雙字節(jié)Unicode字符。    

   使用長字符串    
    如果只簡單地用String定義字符串,,那么該字符串可能是短字符串也可能是ANSI長字符串,,這取決于$H編譯指令的值,$H+(確?。┐黹L字符串(ANSIString類型),。長字符串是Delphi庫中控件使用的字符串。  
    Delphi長字符串基于引用計數(shù)機(jī)制,,通過引用計數(shù)追蹤內(nèi)存中引用同一字符串的字符串變量,,當(dāng)字符串不再使用時,也就是說引用計數(shù)為零時,,釋放內(nèi)存,。   
    如果你要增加字符串的長度,而該字符串鄰近又沒有空閑的內(nèi)存,,即在同一存儲單元字符串已沒有擴(kuò)展的余地,,這時字符串必須被完整地拷貝到另一個存儲單元。當(dāng)這種情況發(fā)生時,,Delphi運(yùn)行時間支持程序會以完全透明的方式為字符串重新分配內(nèi)存,。為了有效地分配所需的存儲空間,你可以用SetLength過程設(shè)定字符串的最大長度值,如:   
    SetLength   (String1,   200);  
    SetLength過程只是完成一個內(nèi)存請求,,并沒有實際分配內(nèi)存,。它只是把將來所需的內(nèi)存預(yù)留出來,,實際上并沒有使用這段內(nèi)存。這一技術(shù)源于Windows操作系統(tǒng),,現(xiàn)被
Delphi用來動態(tài)分配內(nèi)存,。例如,當(dāng)你請求一個很大的數(shù)組時,,系統(tǒng)會將數(shù)組內(nèi)存預(yù)留出來,,但并沒有把內(nèi)存分配給數(shù)組。
  一般不需要設(shè)置字符串的長度,,不過當(dāng)需要把長字符串作為參數(shù)傳遞給API函數(shù)時(經(jīng)過類型轉(zhuǎn)換后),,你必須用SetLength為該字符串預(yù)留內(nèi)存空間,這一點我會在后面進(jìn)行說明,。
  看一看內(nèi)存中的字符串    
  為了幫你更好地理解字符串的內(nèi)存管理細(xì)節(jié),,我寫了一個簡例StrRef。在程序中我聲明了兩個全程字符串:Str1和Str2,,當(dāng)按下第一個按鈕時,,程序把一個字符串常量賦給第一個變量,然后把第一個變量賦給第二個:  
  Str1   :=   'Hello';  
  Str2   :=   Str1;  
  除了字符串操作外,,程序還用下面的StringStatus函數(shù)在一個列表框中顯示字符串的內(nèi)部狀態(tài):  
  function   StringStatus   (const   Str:   string):   string;  
  begin  
  Result   :=   'Address:   '   +   IntToStr   (Integer   (Str))   +  
  ',   Length:   '   +   IntToStr   (Length   (Str))   +    
  ',   References:   '   +   IntToStr   (PInteger   (Integer   (Str)   -   8)^)   +  
  ',   Value:   '   +   Str;  
  end;  
  在StringStatus函數(shù)中,,用常量參數(shù)傳遞字符串至關(guān)重要。用拷貝方式(值參)傳遞會引起副作用,,因為函數(shù)執(zhí)行過程中會產(chǎn)生一個對字符串的額外引用,;與此相反,通過引用(var)或常量(const)參數(shù)傳遞不會產(chǎn)生這種情況,。由于本例不希望字符串被修改,,因此選用常量參數(shù)。        為獲取字符串內(nèi)存地址(有利于識別串的實際內(nèi)容也有助于觀察兩個不同的串變量是否引用了同一內(nèi)存區(qū)),,我通過類型映射把字符串類型強(qiáng)行轉(zhuǎn)換為整型,。字符串實際上是引用,也就是指針:字符串變量保存的是字符串的實際內(nèi)存地址,。  
  為了提取引用計數(shù)信息,,我利用了一個鮮為人知的事實:即字符串長度和引用計數(shù)信息實際上保存在字符串中,位于實際內(nèi)容和字符串變量所指的內(nèi)存位置之前,其負(fù)偏移量對字符串長度來說是-4(用Length函數(shù)很容易得到這個值),,對引用記數(shù)來說是-8,。  
  不過必須記住,,以上關(guān)于偏移量的內(nèi)部信息在未來的Delphi版本中可能會變,,沒有寫入正式Delphi文檔的特性很難保證將來不變。      
  通過運(yùn)行這個例子,,你會看到兩個串內(nèi)容相同,、內(nèi)存位置相同,、引用記數(shù)為2,如圖7.1中列表框上部所示?,F(xiàn)在,,如果你改變其中一個字符串的值,那么更新后字符串的內(nèi)存地址將會改變,。這是copy-on-write技術(shù)的結(jié)果,。      
  第二個按鈕(Change)的OnClick事件代碼如下,結(jié)果如圖7.1列表框第二部分所示:  
  procedure   TFormStrRef.BtnChangeClick(Sender:   TObject);  
  begin  
  Str1   [2]   :=   'a';  
  ListBox1.Items.Add   ('Str1   [2]   :=   ''a''');  
  ListBox1.Items.Add   ('Str1   -   '   +   StringStatus   (Str1));  
  ListBox1.Items.Add   ('Str2   -   '   +   StringStatus   (Str2));  
  end;  
  注意,,BtnChangeClick只能在執(zhí)行完BtnAssignClick后才能執(zhí)行,。為此,程序啟動后第二個按鈕不能用(按鈕的Enabled屬性設(shè)成False),;第一個方法結(jié)束后激活第二個按鈕,。你可以自由地擴(kuò)展這個例子,用StringStatus函數(shù)探究其它情況下長字符串的特性,。 

  動態(tài)分配可以用任意一個分配內(nèi)存的函數(shù), 其實系統(tǒng)最終調(diào)用的都是GetMem, 其它的New,、AllocMem、SetLength等等只不過除了調(diào)用GetMem外還做了一些初始化處理比如把內(nèi)存清零,。釋放可以用Dispose或者FreeMem, 系統(tǒng)最終都是調(diào)用FreeMem的, Dispose相當(dāng)于Finalize(p);   FreeMem(p);  
  Finalize的作用簡單說就是自動釋放結(jié)構(gòu)或者數(shù)組中的string和動態(tài)數(shù)組,   FreeMem則是直接釋放指針?biāo)赶虻膬?nèi)存,例如:  
type  
    TMyRec   =   record  
        Name:   string;  
        X,   Y:   Integer;  
    end;  
    PMyRec   =   ^TMyRec;  
var  
    MyRec   :   PMyRec;  
begin  
    New(MyRec);           //   編譯器會根據(jù)MyRec的大小自動計算需要分配的內(nèi)存數(shù)量然后生成代碼調(diào)用GetMem并將其中的Name字段清零  
    MyRec.Name   :=   str1   +   str2;  
    Dispose(MyRec);     //   除了調(diào)用FreeMem釋放MyRec這個結(jié)構(gòu)的內(nèi)存外還會自動清除其中的Name所用到的內(nèi)存(如果Name指向的string引用計數(shù)=1時);  
//   FreeMem(MyRec);   <--   如果直接調(diào)用FreeMem釋放MyRec,   則會造成內(nèi)存泄露,   因為MyRec.Name指向的字符串沒有釋放(引用計數(shù)-1)  
end;  

 

由于delphi關(guān)于string的內(nèi)存管理的特殊性, 可以有很多技巧充分利用其優(yōu)點生成非常高效的代碼, 比如要用TList來保存string(不是TStringList),   一般的做法是TList.Items[i]中保存一個PString指針, 這樣就需要重新分配一塊內(nèi)存并復(fù)制原串, 大數(shù)據(jù)量的情況下效率很低, 但是如果充分利用string的引用計數(shù)和強(qiáng)制類型轉(zhuǎn)換技巧, 可以直接將string作為指針保存在TList.Items[i]中:   比如:  
  var  
      List:   TList;  
      GlobalString1,   GlobalString2:   string;  
    ...  
  procedure   Test;  
  var  
      tmp:   string;  
  begin  
      tmp   :=   GlobalString1+GlobalString2;  
     List.Add(Pointer(tmp));     //   將tmp作為指針保存進(jìn)List  

{  由于Test過程結(jié)束時會自動釋放掉tmp, 如果直接退出的話List中就保存了一個無效的指針了, 所以這里要欺騙編譯器, 讓它認(rèn)為tmp已經(jīng)被釋放掉了, 等于在不改動tmp引用計數(shù)(當(dāng)前是1)的情況下執(zhí)行相當(dāng)于tmp   :=  ''的語句, 由于直接tmp   := ''會修改引用計數(shù)并可能釋放掉內(nèi)存, 所以用強(qiáng)制類型轉(zhuǎn)換將tmp轉(zhuǎn)成一個Integer并將這個Integer設(shè)置成0(也就是nil), 此語句完全等價于pointer(tmp) :=   nil; 只是個人喜好我喜歡用Integer(tmp) := 0而已.  
}
     Integer(tmp)   :=   0;   

  end;

 

1. string是Delphi編譯器內(nèi)在支持的(predefined or built-in),,是Delphi的一個基本數(shù)據(jù)類型,而PChar只是一個指向零終止字符串的指針,;  
2. String 所存字符串是在堆分配內(nèi)存的,,String變量實際上是指向零終止字符串的指針,與此同時它還具有引用計數(shù)(reference count)功能,,并且自身保存字符串長度,,當(dāng)引用計數(shù)為零時,自動釋放所占用的空間,。  
3.將string賦值給另一個string,,只是一個簡單的指針賦值,不產(chǎn)生copy動作,,只是增加string的引用計數(shù),;  
4.將一個PChar變量類型賦值給一個string   變量類型會產(chǎn)生真正的Copy動作,即將PChar所指向的字符串整個copy到為string分配的內(nèi)存中,;  
5.將string賦值給一個PChar變量類型,,只是簡單地將string的指針值賦值給PChar變量類型,而string的引用計數(shù)并不因此操作而發(fā)生變化,,因為這種情況PChar會對string產(chǎn)生依賴,,當(dāng)string的引用計數(shù)為零自動釋放內(nèi)存空間后,PChar很可能指向一個無效的內(nèi)存地址,,在你的程序你必須小心對付這種情況,。  
6.對PChar的操作速度要遠(yuǎn)遠(yuǎn)高于對string操作的速度,,但PChar是一種落后的管理字符串的方式,而string則以高效的管理而勝出,,PChar它的存在只是為了兼容早期的類型和操作系統(tǒng)(調(diào)用Windows API時會經(jīng)常用到),,建議平常使用string。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點擊一鍵舉報,。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多