如何交换指针?

时间:2014-06-25 17:51:41

标签: delphi delphi-xe6

如何在Delphi中有效地交换指针?我试图交换整数类型的指针。以下示例有效,但使用64位编译时,I2为0。

program Project11;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils;

procedure Swap(const P1, P2);
asm
{$if defined(CPUX86)}
  xchg ecx, [eax]
  xchg ecx, [edx]
  xchg [eax], ecx
{$else}
  xchg rcx, [rax]
  xchg rcx, [rdx]
  xchg [rax], rcx
{$endif}
end;

var
  I1, I2: Integer;

begin
  I1 := 19;
  I2 := 564;

  WriteLn('Swapping..');

  WriteLn('I1: '+I1.ToString());
  WriteLn('I2: '+I2.ToString());

  Swap(I1, I2);

  WriteLn('I1: '+I1.ToString());
  WriteLn('I2: '+I2.ToString());

  ReadLn;
end.

2 个答案:

答案 0 :(得分:12)

我这样做:

type
  TGeneric = class
  public
    class procedure Swap<T>(var Left, Right: T); static;
  end;

class procedure TGeneric.Swap<T>(var Left, Right: T);
var
  temp: T;
begin
  temp := Left;
  Left := Right;
  Right := temp;
end;

这可用于交换任何类型的值。一种能让任何Java程序员哭泣的功能! ; - )

在我看来,编译器可以生成非常高效的代码。我认为您不必担心编译器能够生成有效的代码来执行分配。

对于T = Pointer的32位,我们有:

push ebx
mov ecx,[eax]
mov ebx,[edx]
mov [eax],ebx
mov [edx],ecx
pop ebx
ret 

对于T = Pointer的64位,我们有:

mov rax,[rcx]
mov r8,[rdx]
mov [rcx],r8
mov [rdx],rax
ret

FWIW,我并不热衷于使用const参数的版本。问题中的代码应使用var。但这种通用版本的类型安全性当然更可取。

答案 1 :(得分:5)

大卫的回答完全是解决问题的最明智的方法。

要探究为什么你的代码不起作用,请注意两件事

  • Integer是x86和x64
  • 的32位类型
  • 在x64中,参数(按顺序)传递给RCXRDXR8R9

编译x86时,您的代码可以运行。在编译X64时,您假设参数传递给RAXRDX,(如x86的EAXEDX)这是错误的。所以你的代码看起来应该是

  xchg rax, [rcx]
  xchg rax, [rdx]
  xchg [rcx], rax

您会注意到,如果没有更改,这也会失败 - 现在I1I2都为零。这是因为您假设Integer是64位类型,与指针不同,它不是(并且您没有进行任何类型检查 - 再次看到David的解决方案的好处)。如果你重新定义:

var
  {$if defined(CPUX86)}
    I1, I2: Integer;
  {$else}
    I1, I2: Int64;    
  {$ifend}

然后突然一切都按预期工作。到目前为止,还应该清楚为什么这不是最优雅的方法。