5

如何在 Delphi 中有效地交换指针?我正在尝试交换整数类型的指针。以下示例有效,但I2使用 64 位编译时为 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.
4

2 回答 2

11

我这样做:

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 程序员都哭泣的能力!;-)

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

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

推 ebx
mov ecx,[eax]
mov ebx,[edx]
mov [eax],ebx
mov [edx],ecx
流行音乐
ret

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

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

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

于 2014-06-25T18:14:43.803 回答
5

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

但是,要探索为什么您的代码不起作用,请注意两件事

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

为 x86 编译时,您的代码可以工作。为 X64 编译时,您假设参数被传递给RAXand RDX(如 x86 的EAX, EDX),这是错误的。所以你的代码应该看起来像

  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}

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

于 2014-06-25T19:16:47.930 回答