delphi中move函數的正確理解(const和var一樣,都是傳地址,所以Move是傳地址,而恰恰不是傳值)太精彩了 good

來源:互聯網
上載者:User

標籤:空間   另一個   為什麼   pch   學習   exit   http   構造   資料存放區   

我們能看到以下代碼
var pSource,pDest:PChar;
     len: integer;
.......................//一些代碼
Move(pSource,pDest,len); //錯誤
Move(pSource^,pDest^,len); //正確
看起來確實好像是傳值,而不是傳地址,但是各位別忘了,這不是C,C++,而是Delphi
Object Pascal,所以,絕不能從函數調用的方法判斷是傳值還是串地址!!必須看函數的
定義,只有定義才能說明是傳值還是傳地址,再說一遍,這不是C,C++!!
我們看到的函數定義是這樣的
procedure Move(const Source; var Dest; Count: Integer);
從定義上看,很清楚,Dest是傳地址,而不是傳值,那麼Source呢,其實大家不太清楚
這裡的Const修飾符有兩個含義,第一個大家都知道就是Source一常量方式在函數體內,
不可以改變它的值,第二個可能知道的人不多,那就是Source的傳遞方式和Dest一樣,
是傳地址!也就是說const和var一樣,都是傳地址,只不過一個在函數內不允許修改,
另一個是修改後影響調用的變數值
所以Move是傳地址,而恰恰不是傳值!

 

通過一段時間的學習,上面的理解是不全對的。 其實Const 關鍵詞修飾的 Source是傳值還是傳地址,要看Const修飾的參數類型。如果參數是簡單類型,那還是傳值,如果修飾的參數不是簡單類型,而是字串(不包括短字串),傳的是地址,是實在參數的記憶體位址,即使沒有任何關鍵字修飾,如 Procedure ProcA(s:string),也是傳的是實參的地址,通過引用計數實現,只要在改寫S的值,才寫複製。 Var關鍵詞修飾的變數肯定是傳地址。對於Delphi中資料類型,我們知道聲明一個變數分為兩部分:”變數自身“和”變數的記憶體佔用“。對於簡單類型的變數,變數本身儲存的就是變數值;而對於複雜類型(構造類型或字串類型),則”變數自身“僅僅存放”變數的記憶體佔用“的指標,而”變數的記憶體佔用“有自己的儲存空間,有地址值。就是說,複雜類型的”變數自身“存放的是實際資料所在地址的指標,一個4位元組大小的整數值。  

   Var

      I:integer;

      s:string;

   begin

           I:=1;// 在記憶體中I的地址裡面的值就是 1,即變數本身儲存的就是變數值;

           s:=‘HELLO,WORLD!‘; // 變數本身 存放的是 ‘HELLO,WORLD!’在記憶體中存放的地址值,其實是其首地址。

   end;

 

 我們再來看 Move 函數的聲明:

   procedure  Move( const Source; var Dest; count : Integer );

其內部實現代碼:

  procedure  Move( const Source; var Dest; count : Integer );
var
  S, D: PChar;
  I: Integer;
begin
  S := PChar(@Source);//取的是 Source變數的地址
  D := PChar(@Dest);   //取的是 Dest變數的地址
  if S = D then Exit;
  if Cardinal(D) > Cardinal(S) then
    for I := count-1 downto 0 do
      D[I] := S[I]
  else
    for I := 0 to count-1 do
      D[I] := S[I];
end;

 

現在明白為什麼 Move(pSource,pDest,len); 是錯誤的 ,而  Move(pSource^,pDest^,len); 是正確的?儘管 pSource,pDest 聲明都是PChar類型,是一個指向字串的以NULL為字串的指標類型的指標變數。如果Move(pSource,pDest,len)這樣寫,在Move的實現代碼中(@pSource)取的是指標本身的地址,而不是指標指向資料的地址。注意是指標本身地址而不是資料存放區的地址 ,如果這樣,顯然是不能實現資料移動的,拿一個指標的地址,是取不到值的;而Move(pSource^,pDest^,len); 正確,是傳給函數 PChar變數的指向的地址值。pSource^,指示的是指向的實際的資料的地址。(@取出變數地址運算子);pDest^,指向目的記憶體位址,Move 函數,就知道要從哪個地址開始,移動多少位元組的資料到目的地址空間了,不看Move 函數的內部實現,還真不好理解其實現過程。

通過上面的分析,我們現在回過頭來,再看看Move過程的聲明,procedure  Move( const Source; var Dest; count : Integer ); const 修飾的變數,傳的實參的地址值,之所以使用Const 修飾,是不允許函數內部,修改源記憶體位址,即不能將Source指向別的地址,Source的值是指向pSource^,是pSource^值的拷貝。

Souce是一個記憶體首地址,這個記憶體首地址,當然是一個值,而不是實參數的地址。它傳的還是值,這個值是變數指向的實際資料所在記憶體位址。Const 和 Var 根本就不是一樣的。對於Const 修飾指標時,理解要特別注意。可以參考 http://rainux.org/delphi 和  http://www.cnblogs.com/sonicit/archive/2008/03/23/1118524.html ,裡面講的很詳細。

      總結:按值傳遞一個指標類型的參數,並且用Const 修飾。情況會變得複雜而又很有意思,實際傳遞的是指標的拷貝,形參和實參是兩個指標。不過這兩個指標指向了相同的地址。它們可以共用指標向地址中的資料,但是不能共用指標本身的指向。而引用傳遞的,形參和實參是同一個變數。因而完全共用。  Source   and   Dest都是指向一個首地址,但是不能說 Const 修飾的參數是按地址傳遞,這是概念的錯誤。

看下面的例子:

 

注意區別:

 

procedure TForm1.ByConstVal(const  obj:TEdit)

begin

 obj.Text:=‘Hello World!‘;// 正確 修改 Obj的屬性 ;

 obj:=Form1;// 錯誤,不能將它指向其它的對象;不能修改指標本身的指向,可以修改指向對象的資料,包括屬性等;

end;   

 

procedure TForm1.ByVal(obj:TEdit)

begin

 obj.Text:=‘Hello World!‘;// 正確 修改 Obj的屬性 ;

 obj:=Form1;// 正確,這個時候Obj指向了其它對象,而不是傳入的對象,它的指向發送了變化,不影響外面實參的指向;

end;

 

procedure TForm1.ByRef(obj:TEdit)

begin

 obj.Text:=‘Hello World!‘;// 正確 修改 Obj的屬性 ;

 obj:=Form1;// 正確,這個時候Obj指向了其它對象,而不是傳入的對象,它的指向發送了變化,影響了外面實參的指向,外面實參也指向了Form1對象;

end; 

 

其它一些參考例子:

 

Move(const   Source;   var   Dest;   Count:   Integer);   
  Source   and   Dest都是指向一個首地址,   
  如   
  var                                                                 首地址   
      P:   Pointer;                                                P^   
      P:   PChar                                                   P^   
      //只要是指標類型就是                                     P^   
      S:   string                                                  S[1];   
      Arr:   array   [0..10]   of   Char;                 Arr[0]   or   Arr   
      Arr:   array   [0..10]   of   TDataRecord     Arr[0]   or   Arr   
      //只要是array   類型就是                              Arr[0]   or   Arr   
      
  明白首地址後,就很容易明白C的memcpy和Delphi的Move   ,或API的MoveMemory/CopyMemory,就是記憶體的相互Copy,只要給出首地址就行了。   
    
  當然在數組中首地址也可以不是0開頭,有時我們對一個record   or   array進行賦值時,是一個個域進行賦值,如下:   
  type   
      TArrChar:   array   [0..3]   of   Char;   
    
  procedure   SetValue(Arr:   TArrChar);   
  var   
      Chars:   array   of   Char;   
  begin   
      SetLength(Chars,   4);   
      Chars[0]   :=   Arr[0];   
      ...   
  end;   
    
  我們可以如此:   
  procedure   SetValue(Arr:   TArrChar);   
  var   
      Chars:   array   of   Char;   
  begin   
      SetLength(Chars,   4);   
      Move(Arr,   Chars,   SizeOf(Char)   *   4);   
  end;   
        
  record類型也可以這樣,不過要注意對string/array類型進行靜態分配空間;   如   
      S:      string[255];   
      Arr:   array   [0..len]   of   TMyType

 

感悟:學習Delphi入門很容易,但是深入其中,需要一個過程。不過在學習的過程中注意這些細節,徹底搞清楚,在實際開發中,就事半功倍。

http://blog.csdn.net/tjb_1216/article/details/4627346

delphi中move函數的正確理解(const和var一樣,都是傳地址,所以Move是傳地址,而恰恰不是傳值)太精彩了 good

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.