總結作者:SkyJacker
貢獻者:小峯,LiuXiao
http://www.cnpack.org
CnPack IV QQ Group: 130970
2007-01-31
(轉貼請註明作者、出處且保持完整)
Q:
procedure Move(const Source; var Dest; Count: Integer);
問一下這裏的參數Source和Dest沒有說明數據類型,那具體應該如何使用啊?
var
xx, yy: array [0..6] of Char;
begin
FillChar(xx, 7, #0);
xx := 'abcdef';
Move(xx, yy, 4);
Move(xx[0], yy[0], 4)
//上面這兩條語句有什麼區別
end;
A:
xx, yy就是一塊數據。
xx[0] 就是這塊數據的第一個字符
這兩個的起始地址相同,因此move的結果也相同了。
測試代碼:
procedure TForm1.btnXClick(Sender: TObject);
var
xx, yy: array [0..6] of Char; // 內容存在棧裏
a: array of Char; // 內容存在堆裏,這就可以理解爲什麼動態數組a代表的是字符串的指針了
begin
SetLength(a, 10);
FillChar(xx, 7, #0);
xx := 'abcdef';
a[0]:='A';
a[1]:='B';
Move(xx, yy, 4);
Move(xx[0], yy[0], 4);
Move(xx,a[0],4);
end;
順便再解釋一下 Move 的 Pascal 源碼(Windows XP Sp2 Delphi6 + Update2 ):
procedure Move( const Source; var Dest; count : Integer );
{$IFDEF PUREPASCAL}
var
S, D: PChar;
I: Integer;
begin
S := PChar(@Source);
D := PChar(@Dest);
if S = D then Exit;
if Cardinal(D) > Cardinal(S) then //精華1:小心,別覆蓋了Source
for I := count-1 downto 0 do
D[I] := S[I]
else
for I := 0 to count-1 do
D[I] := S[I];
end;
{$ELSE}
asm
{ ->EAX Pointer to source }
{ EDX Pointer to destination }
{ ECX Count }
PUSH ESI
PUSH EDI
MOV ESI,EAX
MOV EDI,EDX
MOV EAX,ECX
CMP EDI,ESI
JA @@down
JE @@exit
SAR ECX,2 { copy count DIV 4 dwords }
JS @@exit
REP MOVSD
MOV ECX,EAX //精華2: 放之四海皆準。
AND ECX,03H
REP MOVSB { copy count MOD 4 bytes }
JMP @@exit
@@down:
LEA ESI,[ESI+ECX-4] { point ESI to last dword of source }
LEA EDI,[EDI+ECX-4] { point EDI to last dword of dest }
SAR ECX,2 { copy count DIV 4 dwords }
JS @@exit
STD
REP MOVSD
MOV ECX,EAX
AND ECX,03H { copy count MOD 4 bytes }
ADD ESI,4-1 { point to last byte of rest }
ADD EDI,4-1
REP MOVSB
CLD
@@exit:
POP EDI
POP ESI
end;
{$ENDIF}
注:
C/Pascal等高級語言裏頭的指針其實是一個存儲了地址的內存單元,其存儲的內容(是一個地址)所指的地方纔是這個指針所指的內容。
而I:Integer只是一個內存單元,I的內容是整型變量,@I才能得到I的地址。
Pascal中的無類型變量,相當於C中的void。參數const Source; var Dest; 可以理解爲兩塊內存區域。這兩參數調用時在彙編中的實現,便是將Source和Dest的“地址值”作爲參數傳入,而並非傳入這兩變量本身。
但在函數內部的Pascal實現中,Source是傳入的地址再指了一次而得到的結果,因此仍然代表傳進來之前的Source。欲取得彙編中實現時傳入來的地址,則需要用@Source。也就是說,這個var封裝並且隱藏了函數被調用時傳入的取地址操作和函數體內部使用時指了一次的操作。