CnPack 开源软件项目 - 如何理解Move参数中的const Source和var Dest
  网站首页 下载中心 每日构建 文档中心 捐助我们 开发论坛 关于我们 致谢名单 English


 Google 搜索

内容: 
 最新下载


 
CnWizards 1.5.1.1219
[2024-11-03]

 
CnVCL 组件包 20241103
[2024-11-03]

 
CnPack 密码算法库 20241103
[2024-11-03]
  每日构建版下载
  专家包时间线
 项目相关链接


 
CnPack GitHub 首页
GIT 使用说明
申请加入 CnPack
CnPack 成员名单
 网站访问量

今日首页访问: 486
今日页面流量: 2905
全部首页访问: 5296423
全部页面流量: 21341028
建站日期: 2003-09-01

 
如何理解Move参数中的const Source和var Dest
 
CnPack 开源软件项目 2007-01-31 15:58:02

CnPack Tip#1 如何理解Move参数中的const Source和var Dest

总结作者: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封装并且隐藏了函数被调用时传入的取地址操作和函数体内部使用时指了一次的操作。



本文已阅读 12800 次
来自: CnPack 开源软件项目

上一主题 | 返回上级下一主题

相关主题:


版权所有(C) 2001-2024 CnPack 开发组 网站编写:Zhou Jinyu