Некорректое присвоение элементов динамического массива

Здравствуйте

При выполнении этой программы я ожидаю однократной записи в индекс массива A1[1], в реальности же записываются два индекса - [0] и [1]. для меня это выглядит странно. Прошу помочь разобраться.

Спасибо

program Test;

procedure OMG(var A1: array of array of integer); 
  begin
    
    for var i := 0 to 0 do 
    begin
     
      A1[1][1] := 1;  
      Println('test');
     
    end;
  end;


begin
  Var ARR: array of array of integer;
  
  Setlength(ARR, 2);
  
  
  ARR[0] := ArrFill(5, 1234567);
  ARR[1] := ARR[0];
  
  
  OMG(ARR);
  
end.

Снимок

Типы данных делятся на размерные (записи, record) и ссылочные (классы, class).

Переменные размерных типов хранят само содержимое. Они могут, потому что их размер известен ещё при компиляции, поэтому можно заранее рассчитать как они расположатся в ограниченной памяти на стеке (stack, где хранится информация о всех рекурсивно вызванных подпрограммах).

А переменные ссылочных типов хранят в себе ссылку (управляемый указатель), указывающую в кучу (heap - вся остальная оперативка, SOH+LOH). Память в куче выделяется динамически при выполнении, поэтому может иметь вычисленный при выполнении объём - как в случае массивов.

Когда вы делаете присвоение - вы копируете только то что находится в переменной. Если переменная ссылочного типа - копируется ссылка а не содержимое. И получается 2 ссылки, ссылающиеся на один и тот же объект в куче.

Массивы не нужны чтобы увидеть это поведение:

##
var a := new class(x := 5);
var b := a;
b.x := 7;
Print(a);
1 лайк

А это, кстати, в современном коде нет смысла писать.

И статические массивы в данном диалекте паскаля тоже реализованы как классы, хранящие обычный массив внутри, но с доп. синтаксическим сахаром, чтобы при присвоении их копировало.

Спасибо. Всё стало ясно.

И что делать чтобы избежать такой неопределённости? использовать copy()?

Copy это древнепаскальное - тут бы лучше метод .ToArray.

Но это не неопределённость. Это часто полезное поведение, лишь бы брать его в расчёт.

А в изначальном коде, к примеру, можно сделать:

var ARR := ArrGen(2, i->ArrFill(5,1234567));

ArrGen вызывает указанную функцию для каждого генерируемого элемента. Поэтому, получается, ArrFill будет вызвано несколько раз, каждый раз создавая новый массив.

Если нужно значения одного массива присвоить другому. Тут, наверное, ArrFill не подходит? Он же одинаковыми значениями всё запишет. Если считать, что заранее значения копируемого массива неизвестны?

Если действие именно копирование - да, надо скопировать содержимое вместо ссылки, путём создания нового массива. К примеру, как я и сказал - используя .ToArray на существующем массиве.

1 лайк