【问题标题】:How to swap pointers?如何交换指针?
【发布时间】:2014-08-16 09:52:24
【问题描述】:

如何在 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.

【问题讨论】:

  • 在 asm 中实现交换例程不会比仅使用语言环境临时变量带来任何明显的性能提升。如果你想要线程安全,你可以使用TInterlocked.Exchange
  • @StefanGlienke 其实我会用LOCK XCHG
  • 这是 TInterlocked.Exchange 所做的......但它也适用于其他平台。

标签: delphi delphi-xe6


【解决方案1】:

我是这样做的:

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 位,我们有:

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

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

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

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

【讨论】:

  • 这可行,但它不是类型安全的。所以我会避免这种情况。最好让编译器在可能的地方找到你的错误。
  • 嗯。我不称之为高效代码。 ASM 版本将围绕此运行。 MOVXCHG 慢。
  • @user3764855 你的时间安排是什么?
  • 4x MOV + 2x POP vs 3x XCHG 算一下。 :)
  • 你有计时吗?计数操作并不能测量实际运行时间。我想瓶颈是记忆的阅读。
【解决方案2】:

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

要了解为什么您的代码不起作用,请注意两点

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

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

【讨论】:

  • 我猜像xchg eax, [rcx] xchg eax, [rdx] 这样的东西会做一个 32 位交换 xchg [rcx], eax
  • 使用NativeInt会更好。
  • @user3764855 最好使用类型安全的东西。
  • @DavidHeffernan 是的,但我认为 OP 想要交换指针(如问题所示)。令人困惑的部分是选择值类型 (Integer) 来演示方法,而不是引用类型或指针(对于给定的目标平台,两者至少都保证为 NativeInt 大小)。跨度>
  • 我猜我们不是建议 Int64,而是切换到指针,并且可以取消条件代码。是的,整数的选择很奇怪。
猜你喜欢
  • 2021-05-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-21
  • 2012-10-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多