CnPack Forum » 技术板块灌水区 » CnPack Tip#1 如何理解Move


2007-1-31 14:17 skyjacker
CnPack Tip#1 如何理解Move

CnPack Tip#1 如何理解Move
Thx: 小峰
[color=#333333][url=http://www.cnpack.org/][font=宋体][size=3][color=#800080]http://www.cnpack.org[/color][/size][/font][/url]
[font=宋体][size=3]CnPack IV  QQ Group: 130970
2007-01-31[/size][/font][/color]

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
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}

注:
Passion:
C/Pascal等高级语言里头的指针其实是一个存储了地址的内存单元,
其存储的内容(是一个地址)所指的地方才是这个指针所指的内容。
而I:Integer只是一个内存单元,I的内容是整型变量,没有地址的说法。
@I才能得到I的地址。

[[i] 本帖最后由 skyjacker 于 2007-1-31 15:50 编辑 [/i]]

2007-1-31 15:52 Passion
再补充一句,Pascal中的无类型变量,相当于C中的void。
参数const Source; var Dest; 可以理解为两块内存区域。这两参数调用时在汇编中的实现,便是将Source和Dest的"地址值"作为参数传入,而并非传入这两变量本身。
但在函数内部的Pascal实现中,Source是传入的地址再指了一次而得到的结果,因此仍然代表传进来之前的Source。欲取得汇编中实现时传入来的地址,则需要用@Source.

也就是说,这个var封装并且隐藏了函数被调用时传入的取地址操作和函数体内部使用时指了一次的操作。

2007-2-1 10:07 kendling
好!!又一帖强帖!

2007-2-1 10:19 jAmEs_
好像CnPack IV讨论很活跃?怎么QQ组不支持多点人。。。

2007-2-1 10:22 Passion
jAmEs想到IV群来体验体验不?:lol:

2007-2-1 10:34 crystal999
he MoveMemory function moves a block of memory from one location to another.

VOID MoveMemory (

    PVOID Destination,        // address of move destination
    CONST VOID *Source,        // address of block to move
    DWORD Length         // size, in bytes, of block to move  
   );       


Parameters

Destination

Points to the starting address of the destination of the move.

Source

Points to the starting address of the block of memory to move.

Length

Specifies the size, in bytes, of the block of memory to move.



Return Values

This function has no return value.

Remarks

The source and destination blocks may overlap.
如上是系统API
不过delphi直接写得话
procedure MoveMemory(Destination: Pointer; Source: Pointer; Length: DWORD);
begin
  Move(Source^, Destination^, Length);
end;

含义就出来了,拷贝内存而已

2007-2-6 09:25 kendling
兄弟们有没有测试过是D的函数快还是WinAPI快呢?

页: [1]
查看完整版本: CnPack Tip#1 如何理解Move


Powered by Discuz! Archiver 5.0.0  © 2001-2006 Comsenz Inc.