Arrays 如何交换一个二维数组的两行,为什么它可以工作?

Arrays 如何交换一个二维数组的两行,为什么它可以工作?,arrays,delphi,row,swap,Arrays,Delphi,Row,Swap,总结: 请查看以下David、Uwe和其他专家的评论 ================================================================================ 下面的代码交换双值的二维动态数组中的两行。我想知道:(1)以下代码是否是交换二维数组的两行的最佳实践?如果没有,那么做这类工作的最佳实践是什么?(2) 为什么下面的代码可以工作?我的意思是,二维数组不是一个连续的内存段吗?以下代码是否只有靠运气才能工作?任何建议都将不胜感激

总结:

请查看以下David、Uwe和其他专家的评论

================================================================================

下面的代码交换双值的二维动态数组中的两行。我想知道:(1)以下代码是否是交换二维数组的两行的最佳实践?如果没有,那么做这类工作的最佳实践是什么?(2) 为什么下面的代码可以工作?我的意思是,二维数组不是一个连续的内存段吗?以下代码是否只有靠运气才能工作?任何建议都将不胜感激

    unit Unit5;

    interface

    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs;

    type
      TAADouble = array of array of Double;

      TForm5 = class(TForm)
        procedure FormShow(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;

    var
      Form5: TForm5;

    procedure SwapRows(arr: TAADouble; row0, row1: Integer);

    implementation

    {$R *.dfm}

    procedure SwapRows(arr: TAADouble; row0, row1: Integer);
    var
      Tmp: Integer;
    begin
      {$IFDEF FPC}
      Tmp := PtrUInt(arr[row0]);
      PtrUInt(arr[row0]) := PtrUInt(arr[row1]);
      PtrUInt(arr[row1]) := Tmp;
      {$ELSE}
      Tmp := Integer(arr[row0]);
      Integer(arr[row0]) := Integer(arr[row1]);
      Integer(arr[row1]) := Tmp;
      {$ENDIF}

    end;

    procedure TForm5.FormShow(Sender: TObject);
    var
      tmpArray: TAADouble;
      I, J: Integer;
      rowStr: string;
    begin
      SetLength(tmpArray, 10, 10);
      rowStr := '';

      for I := 0 to 9 do
        for J := 0 to 9 do
          tmpArray[I][J] := I * J;

      for I := 0 to 9 do
      begin
        rowStr := '';
        for J := 0 to 9 do
          rowStr := rowStr + FloatToStr(tmpArray[I][J]) + '  ';
        OutputDebugString(PWideChar(rowStr));
      end;

      SwapRows(tmpArray, 3, 4);

      for I := 0 to 9 do
      begin
        rowStr := '';
        for J := 0 to 9 do
          rowStr := rowStr + FloatToStr(tmpArray[I][J]) + '  ';
        OutputDebugString(PWideChar(rowStr));
      end;
    end;

    end.
你问:

以下代码是否仅适用于 运气如何

嗯,是的,您依赖于具体实施的细节

事实上,正确的写作方式是非常自然和简单的:

type
  TDoubleArray = array of Double;
  TDoubleMatrix = array of TDoubleArray;

procedure SwapRows(M: TDoubleMatrix; Row1, Row2: Integer);
var
  Temp: TDoubleArray;
begin
  Temp := M[Row1];
  M[Row1] := M[Row2];
  M[Row2] := Temp;
end;
您需要为行声明一个中间类型TDoubleArray,以便在交换例程中执行对Temp的赋值

二维恒定大小数组

array [1..M] of array [1..N] of TMyType
是一个连续的内存块


您拥有的二维动态大小数组不可用。事实上,它甚至可以是参差不齐的,因为行的列数不同。所以你可以有一个三角形矩阵。

一个动态数组被实现为指向表示该数组的内存块的指针。所以二维动态数组实际上是指向指针数组的指针。这就是交换行(-pointer)s实际起作用的原因

请参见David的答案,以获得更清晰的方法

更新: 如果允许您使用泛型,您还可以这样做:

procedure <SomeClassOrRecord>.SwapRows<T>(var arr: TArray<T>; row0, row1: Integer);
var
  Tmp: T;
begin
  Tmp := arr[row0];
  arr[row0] := arr[row1];
  arr[row1] := Tmp;
end;
procedure.SwapRows(var-arr:TArray;第0行,第1行:整数);
变量
Tmp:T;
开始
Tmp:=arr[row0];
arr[row0]:=arr[row1];
arr[row1]:=Tmp;
终止

@Xichen不,不是。我想我这样做是出于习惯。没有它,它将完美地工作。我不觉得有什么令人信服的理由可以使用或不使用var,这只是我的习惯。我经常看到参数“错误地”声明为
var
,因为开发人员认为他们在修改输入。当对象被传递到方法中时,这种情况尤其常见
var
不需要修改对象的状态,但是使用
var
实际上意味着可以用完全不同的实例替换对象。这是按引用传递和按值传递之间的细微差别。动态数组总是通过引用传递,因此
var
允许您替换整个数组。@Craig是的,这正是我所想的。不过,引用类型是对var和const的嘲弄。不知何故,我认为动态数组具有与字符串相同的“写时复制”行为。简单的测试表明我错了。。。但必须注意的是,对字符串执行等效操作需要使用“var”关键字。此外,使用“const”关键字通常允许编译器进行一些额外的优化,在这种情况下,删除对@DynArrayAddRef@Ken的调用。正是动态数组不执行写时拷贝这一事实使得这一切都可以工作。动态数组的写时复制将是灾难性的,因为它们通常有非常大的内存块作为后盾。@Uwe:非常感谢您的宝贵意见@大卫:你能帮我评论一下你所说的
是什么意思吗?如果行的引用计数不同,它就不起作用了。
?@Xichen不,我有点困惑,那句话不正确。大卫的评论消失了,我猜他意识到交换指针也会交换ref计数,因为它们位于数组前面,而不是指向数组的指针。否则裁判计数可能不起作用。@David,@Uwe:谢谢!我将切换到David的方法,因为它不涉及将指针转换为整数,因此我不需要担心integer/Cardinal/NativeInt/NativeUInt/PtrInt/PrtUInt之间的
IfDef
,将指针强制转换为整数是不安全的-当64位版本到达时,这将非常失败。。。如果您使用的是最新的Delphi,请使用NativeInt类型,否则请定义您自己的类型,您可以自行设置以匹配指针大小。