如何交换指针?

use*_*855 5 delphi delphi-xe6

如何在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.
Run Code Online (Sandbox Code Playgroud)

Dav*_*nan 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;
Run Code Online (Sandbox Code Playgroud)

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

在我看来,编译器生成非常高效的代码.我认为你不必担心编译器生成有效代码来执行赋值的能力.

对于32位T = Pointer我们有:

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

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

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

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

  • 这可行,但它不是类型安全的.所以我会避免这种情况.最好让编译器尽可能地找到你的错误. (5认同)

J..*_*... 5

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

要了解您的代码无法正常工作的原因,请注意两件事

  • Integer 是x86和x64的32位类型
  • 在64,参数传递(按顺序)RCX,RDX,R8R9

编译x86时,您的代码可以正常工作.当编译为X64你假设参数被传递给RAXRDX,(像86的EAX,EDX),这是错误的.所以你的代码应该是这样的

  xchg rax, [rcx]
  xchg rax, [rdx]
  xchg [rcx], rax
Run Code Online (Sandbox Code Playgroud)

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

var
  {$if defined(CPUX86)}
    I1, I2: Integer;
  {$else}
    I1, I2: Int64;    
  {$ifend}
Run Code Online (Sandbox Code Playgroud)

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