Почему при заполнении массива структур в 1С все элементы становятся одинаковыми?

Программист 1С v8.3 (Управляемые формы)
← На главную

Часто разработчики сталкиваются с непонятным, на первый взгляд, поведением системы при работе с массивами. Представим ситуацию: вы создаете цикл для заполнения массива структурами, на каждой итерации присваивая полям структуры новые, уникальные значения. В процессе отладки, прямо внутри цикла, вы видите, что каждая добавляемая структура содержит верные данные. Однако, как только цикл завершается, проверка массива показывает, что все его элементы абсолютно одинаковы и равны последней добавленной структуре. Давайте разберемся, почему так происходит и как это исправить.

Эта проблема — одна из классических ошибок, связанных с фундаментальным принципом работы 1С: разницей между ссылочными и значимыми типами данных.

Анализируем проблемный код

Рассмотрим типичный пример кода, который приводит к описанной выше ошибке. Этот код был предложен автором на форуме, и в нем кроется корень проблемы.


// Неправильный код!
Нов = Новый Структура ("Склад, НоменклатураКод");

Для Индекс = 0 по НомКодМас.ВГраница() Цикл
    // Предполагается, что здесь ПолеСклад и ПолеНоменклатураКод получают
    // разные значения на каждой итерации
    Нов.Склад = ПолеСклад;
    Нов.НоменклатураКод = ПолеНоменклатураКод;

    Масс49.Вставить(Инд, Нов);
КонецЦикла;

// После цикла проверка Масс49.Получить(0), Масс49.Получить(1) и т.д.
// покажет одинаковые значения.

Проанализируем ситуацию по шагам, чтобы понять логику выполнения этого кода платформой 1С:

  1. Создание объекта. Строка Нов = Новый Структура(...) выполняется один раз, до начала цикла. В этот момент в памяти создается один-единственный объект типа Структура. Переменная Нов не хранит саму структуру, она хранит ссылку (адрес в памяти) на этот объект.
  2. Работа цикла. На каждой итерации цикла происходит следующее:
    • В поля объекта, на который указывает ссылка Нов, записываются новые значения (Нов.Склад = ...).
    • Методом Масс49.Вставить(Инд, Нов) в массив добавляется значение переменной Нов. А поскольку Нов — это ссылка, то в массив добавляется копия этой ссылки, а не копия самого объекта структуры.
  3. Результат. После завершения цикла все элементы массива Масс49 (с индексами 0, 1, 2 и т.д.) содержат одну и ту же ссылку, которая указывает на один и тот же объект структуры в памяти. Этот объект, в свою очередь, хранит значения, которые были ему присвоены на последней итерации цикла. Поэтому, обращаясь к любому элементу массива, вы по факту обращаетесь к одному и тому же объекту и видите его последние данные.

Можно провести простую аналогию. Представьте, что у вас есть один блокнот (объект Структура). Вы пишете на его первой странице "задача 1", а затем делаете 10 фотографий этой страницы и складываете их в папку (Массив). Затем вы стираете надпись и пишете в том же блокноте "задача 2", и снова делаете 10 фотографий. Если вы так сделаете 10 раз, то в конце в вашем блокноте будет написано "задача 10". А теперь посмотрите на ваши фотографии. Все они будут одинаковыми, потому что вы фотографировали один и тот же блокнот. Проблема в том, что вы меняли содержимое одного блокнота, а нужно было каждый раз брать новый.

Правильное решение: создание нового объекта в цикле

Чтобы исправить ошибку, необходимо на каждой итерации цикла создавать новый, независимый экземпляр структуры. Для этого достаточно перенести строку создания объекта внутрь цикла.

Вот как выглядит исправленный и корректный код:


// Правильный код!
Для Индекс = 0 по НомКодМас.ВГраница() Цикл
    // Создаем НОВЫЙ объект структуры на КАЖДОЙ итерации
    Нов = Новый Структура("Склад, НоменклатураКод"); 

    // Предполагается, что здесь ПолеСклад и ПолеНоменклатураКод получают
    // разные значения на каждой итерации
    Нов.Склад = ПолеСклад; 
    Нов.НоменклатураКод = ПолеНоменклатураКод; 

    // В данном случае лучше использовать метод "Добавить",
    // он проще и эффективнее для последовательного заполнения
    Масс49.Добавить(Нов); 
КонецЦикла;

Теперь на каждом шаге цикла будет создаваться совершенно новый объект Структура в новой области памяти. В массив будет добавляться ссылка уже на этот новый объект. В результате каждый элемент массива будет указывать на свою собственную, уникальную структуру со своими значениями.

Также обратите внимание на замену метода Вставить() на Добавить(). Если вам нужно просто последовательно заполнить массив, метод Добавить() является предпочтительным. Он автоматически добавляет элемент в конец массива, и вам не нужно вручную отслеживать индекс для вставки.

Ключевой момент: ссылочные и значимые типы

Чтобы окончательно закрепить понимание, важно различать два типа данных в 1С:

1. Значимые (примитивные) типы: Число, Строка, Дата, Булево, Неопределено, NULL. Когда вы присваиваете переменную такого типа другой переменной, происходит полное копирование значения.


ПеременнаяА = 10;
ПеременнаяБ = ПеременнаяА; // В ПеременнаяБ скопировалось значение 10

ПеременнаяА = 20; // Изменяем ПеременнаяА

// При этом ПеременнаяБ осталась равна 10, т.к. это была независимая копия
Сообщить(ПеременнаяБ); // Выведет: 10

2. Ссылочные типы: Структура, Массив, Соответствие, ТаблицаЗначений, все объекты базы данных (СправочникОбъект, ДокументОбъект) и т.д. При присваивании копируется только ссылка (адрес) на объект в памяти, а не сам объект.


Структура1 = Новый Структура("Имя", "Иван");
Структура2 = Структура1; // Копируется ССЫЛКА. Обе переменные указывают на ОДИН объект.

Структура2.Имя = "Петр"; // Изменяем данные объекта через вторую переменную

// Данные изменятся и для первой переменной, т.к. объект один и тот же
Сообщить(Структура1.Имя); // Выведет: "Петр"

Именно это и произошло в исходной проблеме: в массив копировались ссылки на один и тот же объект, что и привело к неожиданному результату. Понимание этого механизма является ключом к избежанию множества подобных ошибок при программировании на платформе 1С.

← На главную