I am a bit confused on how deep a multidimensional array is handed over to a procedure. Let's take the following code:
program test;
type
tA = array of array of integer;
var
A : tA = nil;
procedure P1(M : tA);
var i, j : integer;
begin
SetLength(M, 3, 3);
for i := 0 to 2 do
for j := 0 to 2 do
M[i,j] := 100 + i*10 + j;
end;
procedure P2(var M : tA);
var i, j : integer;
begin
SetLength(M, 3, 3);
for i := 0 to 2 do
for j := 0 to 2 do
M[i,j] := 200 + i*10 + j;
end;
procedure P3(const M : tA);
var i,j : integer;
begin
SetLength(M[0], 4);
SetLength(M[1], 4);
SetLength(M[2], 4);
for i := 0 to 2 do
for j := 0 to 3 do
M[i,j] := 300 + i*10 + j;
end;
var i : integer;
begin
SetLength(A, 2, 2);
A[0,0] := 0;
A[0,1] := 1;
A[1,0] := 10;
A[1,1] := 11;
writeln('Original');
for i := 0 to 1 do
writeln(A[i, 0], ' ', A[i, 1]);
writeln('After P1');
P1(A);
for i := 0 to 1 do
writeln(A[i, 0], ' ', A[i, 1]);
writeln('After P2');
P2(A);
for i := 0 to 2 do
writeln(A[i, 0], ' ', A[i, 1], ' ', A[i, 2]);
writeln('After P3');
P3(A);
for i := 0 to 2 do
writeln(A[i, 0], ' ', A[i, 1], ' ', A[i, 2], ' ', A[i, 3]);
readln;
end.
At a first sight it seems logic that after P1 nothing is changed as in that one the parameter is handed over as a value what normally means that the values are copied to the stack and whatever happens with it, it is not reflected any more in the calling place (what might even be just a value, not a variable).
After P2 (where it is handed over as a
var parameter, i.e. as a pointer) it is logic that every change done inside the procedure, it is reflected in A as well.
The confusion comes with P3. If it were a normal variable one would expect that M is not allowed to be touched at all inside the procedure as it is a
const parameter. I read a lot about it, and it is clear that for a multi-dimensional array the variable is a pointer, and the
const protects only the pointer itself, not the content behind it. That is understood, but there are still two strange things with it, what I cannot explain:
1. I cannot change the length of the array itself (
SetLength(M, 5) would fail at compile time). If the pointer points to a structure in memory, it should be possible to change a field in the structure, i.e. the number of rows. If I read correctly, it is because the first dimension (pointers to the row arrays) of a matrix is stored as a continuous memory place, and so changing the size of it,
might change the memory place (and thus the pointer pointing to it) as well (e.g. if it gets bigger and does not fit in the allocated place). In this case, it should give a run-time error, not a compile time error though. So, it seems that although it might
not change the handed over pointer (e.g. if I reduce the size, not increase it) the compiler notices that it is a
potentially dangerous operation and does not allow it. However if it is that smart, then why it does not detect the same for the changing of the length of one row, or for the change of elements inside the rows?
2. Accepting that for a multidimensional array the compiler only "cares" about the first level (the rest is hidden behind the pointers stored in the first level array), I do not understand P1. Based on the above, what would be logic is that the compiler makes a copy of the FIRST LEVEL ONLY when the parameter is handed over. So it will have a copy of the list of pointers pointing to the original rows. Then changing the copied array, it would first go to the copied vector of pointers and would find the copy of the pointer, still pointing to the original row. If then I change e.g. the length of the row, then it would make a new row with a new pointer, hence any change of the elements of the changed row would not appear in the original array. However if I do
not change the row, only an element of the row (as is the case in the example) then it could use the copied pointer pointing to the original row, and hence change the element of the original row. It is not the case. So, it seems that in this particular case when the array is handed over as a parameter, it makes a deep copy of it (either immediately or when it is touched - being managed). That raises the question, if there is a routine deep inside the compiler to make a deep copy of any shape multi dimensional array, then why is it not available and even claimed by TRon that it does not exist:
https://forum.lazarus.freepascal.org/index.php/topic,66313.msg507467.html#msg507467. How is it done then? It would be very useful for other array deep-copy tasks.
What do I miss?