Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to swap pointers?

How can I efficiently swap pointers in Delphi? I am trying to exchange pointers for integer type. The following example works however I2 is 0 when compiled with 64 bit.

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.
like image 810
user3764855 Avatar asked Jun 25 '14 17:06

user3764855


2 Answers

I do it like this:

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;

This can be used to swap values of any type. A capability that will make any Java programmers weep! ;-)

The compiler produces pretty efficient code in my opinion. I don't think you need to worry about the compiler's ability to produce effective code to perform assignments.

For 32 bit with T = Pointer we have:

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

For 64 bit with T = Pointer we have:

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

FWIW, I'm not keen on your versions use of const parameters. The code in the question should use var. But the type safety of this generic version is of course preferable.

like image 91
David Heffernan Avatar answered Sep 28 '22 15:09

David Heffernan


David's answer is entirely the most sensible approach to solving the problem.

To explore why your code doesn't work however, note two things

  • Integer is a 32-bit type for both x86 and x64
  • In x64, arguments are passed (in order) to RCX, RDX, R8 and R9

When compiling for x86 your code works. When compiling for X64 you are assuming that arguments are being passed to RAX and RDX, (like x86's EAX, EDX) which is wrong. So your code should look like

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

You'll note that without changes this also fails - now both I1 and I2 are zero. This is because you've assumed that Integer is a 64-bit type, which, unlike pointers, it is not (and you are not doing any type checking whatsoever - see again the benefit's of David's solution). If you redefine :

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

Then suddenly everything works as expected. By now, it should also be clear why this is not the most elegant approach.

like image 41
J... Avatar answered Sep 28 '22 14:09

J...