Search code examples
arraysdelphimovedynamic-arrays

How to use move on 2-Dimension Dynamic Arrays of integer/real?


In my Delphi Rio application I use a lot of 2 dimensions Dynamic Arrays. In order to speed up some operations I'd like to use move command instead of copy. I could make it work for 1D dynamic arrays, but for 2D or higher I could not. For the second dimension, after issued move(A,B,size) the elements of array B point to the same memory address of elements of array A, i.e. B references A. In fact I want to work with B separetely of A. See my code :

program ProjArrayMove;

    {$APPTYPE CONSOLE}

    {$R *.res}

    uses
      System.SysUtils;

    Type

         Arrayint   = Tarray<Integer>;
         Arrayreal  = TArray<Real>;
         MatrixInt  = Array of Arrayreal;
    var
        vectorA, vectorB : Arrayint;
        matA, matB       : MatrixInt;
        idx,idy          : integer;
    begin
         TRY
           // =============== TESTING WITH 1D DYNAMIC ARRAYS OF SIMPLE TYPES ==================
              Writeln('==============================================================');
              Writeln('========= TESTING 1-DIMENSION DYNAMIC ARAYS ==================');
              Writeln('===============================================================');

              readln;
              Writeln('------- Fills Vector A ----------------------------');
              SetLength(vectorA,5);
              for idx :=0 to 4 do
              begin
                    vectorA[idx] := (idx+1) * 10;
                    Writeln('VectorA : [' + idx.tostring + ']  '  +
                              Format('Address : %p  [%d]' ,[PPointer(@VectorA), VectorA[idx] ]) );
              end;

              readln;
              Writeln('--------------------------------------------------');
              Writeln('------ Moves VectorA to VectorB ------------------');

              SetLength(VectorB,Length(vectorA));
              Move(VectorA[0],VectorB[0],SizeoF(VectorA[0]) * Length(VectorA));
              for idx :=0 to 4 do
                    Writeln('VectorB : [' + idx.tostring + ']  '  +
                              Format('Address : %p  [%d]' ,[PPointer(@VectorB), VectorB[idx] ]) );

              readln;
              Writeln('---------------------------------------------------');
              Writeln('------ Changes VectorB contents  ------------------');

              for idx :=0 to 4 do
              begin
                    vectorB[idx] := (idx+1) * 200;
                    Writeln('VectorB : [' + idx.tostring + ']  '  +
                              Format('Address : %p  [%d]' ,[PPointer(@VectorB), VectorB[idx] ]) );
              end;

              readln;
              Writeln('--------------------------------------------------');
              Writeln('------ Checking Vector A  ------------------------');

              for idx :=0 to 4 do
                    Writeln('VectorA : [' + idx.tostring + ']  '  +
                              Format('Address : %p  [%d]' ,[PPointer(@VectorA), VectorA[idx] ]) );
              Writeln;
              Writeln('CONCLUSION : ===>>  MOVE command works fine for 1-Dimension Dynamic Arrays!');
              readln;


    //=========================== TESTING WITH MATRIX 2D DYNAMIC ARRAYS OF SIMPLE TYPES ==================

              Writeln('===============================================================');
              Writeln('========= TESTING 2-DIMENSIONS DYNAMIC ARAYS ==================');
              Writeln('===============================================================');
              readln;
              Writeln('------ Fills MatrixA -----------------------------');
              SetLength(matA,5,2);
              for idx :=0 to 4 do
                 for idy := 0 to 1 do
                 begin
                       matA[idx][idy] := (idx +1) * (idy +1);
                        Writeln('Mat A : [' + idx.tostring + '][' + idy.tostring +']  '  +
                                  Format('Address : %p  [%f]' ,[PPointer(@matA[idx][idy]), matA[idx][idy] ] ));
                 end;

              readln;
              Writeln('-------------------------------------------------');
              Writeln('------ Move MatrixA to MatrixB ------------------');

              SetLength(matB,length(matA));
              //move(matA[0],MatB[0],Sizeof(matA[0]) * length(matA));
              move(matA,MatB,Sizeof(matA[0]) * length(matA));
              for idx :=0 to 4 do
              begin
                   Setlength(MatB[idx],length(matA[idx]));
                   //Move(matA[idx][0],matB[idx][0],sizeof(matB[idx][0]) * length(matB[idx]) );
                   Move(matA[idx],matB[idx],sizeof(matB[idx][0]) * length(matB[idx]) );
                   for idy := 0 to 1 do
                   begin
                          Writeln('Mat B : [' + idx.tostring + '][' + idy.tostring +']  '  +
                                  Format('Address : %p  [%f]' ,[PPointer(@matB[idx][idy]), matB[idx][idy] ] ));

                   end;
              end;

              readln;

              Writeln('-------------------------------------------------');
              Writeln('------ Change MatrixB content  ------------------');
              readln;

              for idx :=0 to 4 do
                 for idy := 0 to 1 do
                 begin
                      matB[idx][idy] := 100.5 * (idx+1) * (idy +1);
                      Writeln('Mat B : [' + idx.tostring + '][' + idy.tostring +']  '  +
                              Format('Address : %p  [%f]' ,[PPointer(@matB[idx][idy]), matB[idx][idy] ] ));
                 end;

              Writeln('-------------------------------------------------');
              Writeln('------ Checking Matrix A ------------------------');
              readln;

              for idx :=0 to 4 do
                 for idy := 0 to 1 do
                        Writeln('Mat A : [' + idx.tostring + '][' + idy.tostring +']  '  +
                                 Format('Address : %p  [%f]' ,[PPointer(@matA[idx][idy]), matA[idx][idy] ] ));


              Writeln;
              Writeln('CONCLUSION : ===>>  MOVE command DOES NOT WORK on 2-Dimensions Dynamic Arrays!');
              readln;

          except
            on E: Exception do
                 begin
                       Writeln(E.ClassName, ': ', E.Message);
                       readln;
                 end;
          end;
    end.

Is there something wrong or missing in move command for the second dimension?

Thanks !


Solution

  • Your problems start here:

    Move(matA, matB, Sizeof(matA[0]) * Length(matA));
    

    You are passing a dynamic array to Move. A dynamic array is implemented as a pointer to the first element of the array. You are therefore overwriting the pointer.

    Perhaps you wanted to do something like this:

    Move(matA[0], matB[0], Sizeof(matA[0]) * Length(matA));
    

    But you don't want to do that either. That's using Move on a managed type, which is exactly the same mistake that you made in your previous question.

    In fact if you just remove that line of code it should work. Note I've not checked in detail so there may be other defects.

    Your code is really extremely complex, and it's very hard to see what is going on. At least some of your problems are due to your code being so obfuscated. Try to stop putting huge amounts of code into a single procedure. Split the code up into smaller parts, and then glue these smaller parts together in a driver routine.

    You want to use a function like this:

    function CloneMatrixInt(const matrix: array of ArrayInt): MatrixInt;
    var
      i: Integer;
    begin
      SetLength(Result, Length(matrix));
      for i := 0 to High(Result) do
        Result[i] := Copy(matrix[i]);
    end;
    

    Notice that this is essentially identical to the answer to your previous question. Why is that?

    You need to stop thinking of MatrixInt as a multidimensional array. It's not. It's just an array. It's an array whose elements are in turn also arrays. Technically it is a jagged array. But you just need to think of it as a single dimensional array.

    So the function to clone it operates as follows:

    1. Allocate the destination array.
    2. Loop over the destination array cloning each element from the source array.

    That's exactly the same process as in your previous question. There we had an array of record with a function to clone that record. But because we had separated the two tasks of cloning the outer array from cloning the elements of the array, the resulting code is essentially identical.

    Now imagine you had an array of MatrixInt. Let's say

    type
      ArrayMatrixInt = array of MatrixInt;
    

    Well, clone that like this:

    function CloneArrayMatrixInt(const arrayMatrix: array of MatrixInt): ArrayMatrixInt;
    var
      i: Integer;
    begin
      SetLength(Result, Length(arrayMatrix));
      for i := 0 to High(Result) do
        Result[i] := CloneMatrixInt(matrix[i]);
    end;
    

    Guess what, the exact same code as before!

    Given this question, and your previous one, can I please suggest that you refrain from trying to use Move to solve all problems. There's no reason to suggest that any of the problems you are encountering are going to be solved by using Move.

    One final note. Jagged arrays are expensive to allocate because they require multiple allocations. They are also inefficient to operate on because they are not stored contiguously. Delphi lacks a multidimensional array type, and if ultimate performance is required then you may be best to implement multidimensional array type.