Как устроены переменные во встроенном языке 1С:Предприятия 8.3

· 10 мин

Содержание

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

Объявить переменную можно двумя способами: явно, оператором Перем Имя;, или неявно, первым присваиванием Имя = 0;. Всё, что описано ниже, относится к встроенному языку платформы 8.3 и не зависит от конфигурации: правила одинаковы в Бухгалтерии, ЗУП, УТ и в собственной разработке.

Динамическая типизация: переменная без типа

В 1С нет объявлений вида «целое» или «строка длиной 50». Переменная — это просто имя, к которому привязано значение. Тип определяется в момент присваивания и может смениться в любой строке кода.

Значение = 100;            // Число
Значение = "сто";          // теперь Строка
Значение = ТекущаяДата();  // теперь Дата

Компилятор такой код пропустит. Ошибка, если она есть, проявится при выполнении: например, при попытке сложить дату со строкой.

Чтобы узнать, что лежит в переменной, используют две функции:

  • 🔍 ТипЗнч(Значение) — возвращает тип текущего значения;
  • 🔍 Тип("Число") — возвращает тип по его имени, для сравнения.
Если ТипЗнч(Значение) = Тип("Число") Тогда
	Значение = Значение * 2;
ИначеЕсли ТипЗнч(Значение) = Тип("СправочникСсылка.Номенклатура") Тогда
	Сообщить("Передан товар");
КонецЕсли;

Правила имён

  • ✅ Имя состоит из букв, цифр и символа подчёркивания.
  • ✅ Начинается с буквы или подчёркивания, не с цифры.
  • ✅ Регистр не важен: сумма, Сумма и СУММА — одна и та же переменная.
  • ❌ Нельзя использовать зарезервированные слова: Если, Цикл, Новый, Истина и другие.

Допустимы и русские, и латинские имена. В типовых конфигурациях принят «верблюжий» стиль по-русски: СуммаДокумента, ЕстьОшибки, СтрокаТабличнойЧасти.

Имя булевой переменной удобно начинать с «Есть», «Это», «Нужно»: ЭтоНовый, НужноПерезаписать. Условие Если НужноПерезаписать Тогда читается без комментария.

Чему равна переменная без значения

Переменная, объявленная через Перем и ещё не получившая значения, содержит Неопределено. Неявно объявленная переменная без присваивания не существует вовсе: обращение к ней в выражении — ошибка компиляции.

Простые типы данных в 1С

Простые (примитивные) типы хранят одно значение и копируются при присваивании. Если Б = А, а потом А изменилась, Б останется прежней.

ТипПример литералаЧто важно знать
Число150, -3.75Дробная часть отделяется точкой, не запятой
Строка"ООО ""Ромашка"""Кавычка внутри строки удваивается
Дата'20261007', '20261007153000'Хранит дату и время с точностью до секунды
БулевоИстина, ЛожьРезультат любого сравнения
НеопределеноНеопределено«Значения нет» в языке
NullNULL«Значения нет» в результатах запроса
ТипТип("Строка")Значение, описывающее сам тип

Числа

Деление целых даёт дробь: 7 / 2 вернёт 3.5. Целую часть берут функцией Цел(), остаток — оператором %: 7 % 2 = 1. Округление — Окр(Число, 2).

Строки

Строки складываются оператором +. Многострочный текст пишут с символом | в начале каждой следующей строки:

Текст = "Первая строка
|Вторая строка";

Число со строкой лучше не склеивать напрямую: результат зависит от того, что стоит слева, а число получит разделитель групп разрядов. Надёжнее так:

Номер = 12500;
Текст = "Заказ № " + Формат(Номер, "ЧГ=0");     // "Заказ № 12500"
Текст = СтрШаблон("Заказ № %1", Формат(Номер, "ЧГ=0"));

Даты

Дата в 1С — это момент времени. Прибавление числа добавляет секунды, а разность двух дат тоже возвращается в секундах:

Завтра = ТекущаяДата() + 86400;          // 24 * 60 * 60 = 86400 секунд
ДнейМежду = (ДатаОкончания - ДатаНачала) / 86400;

Пустая дата — '00010101'. Проверять её удобнее функцией ЗначениеЗаполнено(), которая вернёт Ложь для пустой даты, нуля, пустой строки, Неопределено и пустой ссылки.

Неопределено и Null — не одно и то же

  • 🔸 Неопределено возникает в коде: переменная без значения, отсутствующий ключ в соответствии, функция без Возврат.
  • 🔸 NULL приходит из запроса: левое соединение не нашло пары, реквизит есть не у всех типов составного поля.

Сравнение Значение = Неопределено для NULL вернёт Ложь. Для выборки из запроса проверяйте Значение = NULL или избавляйтесь от него в самом запросе функцией ЕСТЬNULL().

Ссылочные типы и объекты метаданных

Каждый справочник, документ, перечисление, план счетов и план видов характеристик порождает в языке свои типы. Для справочника «Номенклатура» это, в частности:

  • 📌 СправочникСсылка.Номенклатура — ссылка на элемент;
  • 📌 СправочникОбъект.Номенклатура — сам элемент, загруженный для изменения;
  • 📌 СправочникМенеджер.Номенклатура — менеджер, доступный как Справочники.Номенклатура.

Ссылка и объект — разные вещи

Ссылка — лёгкое значение, по сути указатель на запись в базе. Её можно хранить, сравнивать, передавать с сервера на клиент. Изменить данные через ссылку нельзя.

Товар = Справочники.Номенклатура.НайтиПоКоду("00-00001");
Если НЕ Товар.Пустая() Тогда
	ТоварОбъект = Товар.ПолучитьОбъект();
	ТоварОбъект.Комментарий = "Проверено";
	ТоварОбъект.Записать();
КонецЕсли;

Если элемент не найден, НайтиПоКоду() вернёт пустую ссылку. Её же даёт Справочники.Номенклатура.ПустаяСсылка().

Обращение через точку к реквизиту ссылки (Товар.Артикул) читает данные из базы. В цикле по тысяче строк это тысяча обращений. Нужны реквизиты многих объектов — получайте их одним запросом.

Почему объект нельзя передать на клиент

Значения, которые уходят с сервера на клиент, должны сериализоваться. Ссылки, простые типы, структуры и массивы из них передаются. СправочникОбъект и ДокументОбъект — нет: на клиенте с ними работают через данные формы.

Где видна переменная: локальные, модульные и глобальные

Область видимости определяется местом объявления.

ВидГде объявленаГде доступнаСколько живёт
ЛокальнаяВнутри процедуры: Перем или присваиваниемТолько в этой процедуреДо выхода из процедуры
Переменная модуляПерем в начале модуляВо всех процедурах модуляПока жив экземпляр модуля
Экспортная переменная модуляПерем Имя Экспорт;Снаружи, через объект или формуПока жив объект
ГлобальнаяПерем Имя Экспорт; в модуле управляемого приложенияВо всех клиентских модуляхВесь сеанс на клиенте

Порядок объявления в модуле

Модуль читается сверху вниз в строгом порядке: раздел объявления переменных, затем процедуры и функции, затем основная программа (операторы вне процедур). Перем после первой процедуры — синтаксическая ошибка.

Перем СчетчикОбработанных;          // переменная модуля
Перем РежимЗагрузки Экспорт;        // видна снаружи

Процедура ОбработатьСтроку(Строка)
	СчетчикОбработанных = СчетчикОбработанных + 1;
КонецПроцедуры

СчетчикОбработанных = 0;            // основная программа: инициализация

Внутри процедуры Перем тоже ставится до первого оператора.

Ловушка с одинаковыми именами

Если в модуле объявлена переменная Итог, то строка Итог = 0; внутри любой процедуры этого модуля изменит модульную переменную, а не создаст локальную. Отсюда «странные» значения, которые переживают вызов процедуры. Локальным переменным давайте имена, которые не пересекаются с модульными.

В каких модулях переменные модуля недоступны

В общих модулях раздела объявления переменных нет. Хранить там состояние между вызовами не получится. Для кэша на сервере используют общий модуль с повторным использованием возвращаемых значений или параметры сеанса.

Глобальные переменные на клиенте и на сервере

Экспортная переменная модуля управляемого приложения доступна всем клиентским модулям: формам, общим клиентским модулям. На сервере такого механизма нет. Серверные вызовы могут выполняться разными рабочими процессами, поэтому общее состояние сеанса на сервере хранят в параметрах сеанса (ПараметрыСеанса.ТекущийПользователь в типовых).

Переменные модуля формы

В модуле формы перед Перем ставится директива компиляции:

&НаКлиенте
Перем КэшЦен;      // живёт, пока открыта форма

Серверная переменная модуля формы не сохраняет значение между серверными вызовами. Если в одном &НаСервере-методе заполнить её, а в другом прочитать, там будет Неопределено. Данные между вызовами храните в реквизитах формы.

Экспортная переменная модуля объекта

Это рабочий приём для передачи флага в обработчики записи:

// модуль объекта документа
Перем ПропуститьПроверкуОстатков Экспорт;

Процедура ОбработкаПроведения(Отказ, РежимПроведения)
	Если ПропуститьПроверкуОстатков = Истина Тогда
		Возврат;
	КонецЕсли;
	// проверка остатков
КонецПроцедуры

Снаружи: ДокОбъект.ПропуститьПроверкуОстатков = Истина; перед ДокОбъект.Записать(РежимЗаписиДокумента.Проведение). Для той же задачи у любого объекта есть встроенная структура ДополнительныеСвойства, и в типовых конфигурациях чаще пользуются ей: не нужно менять модуль объекта.

Параметры процедур и функций: по ссылке и по значению

Параметр — это локальная переменная, которая получает значение при вызове. По умолчанию параметры передаются по ссылке: изменение внутри процедуры меняет переменную вызывающего кода. Ключевое слово Знач включает передачу по значению.

Процедура Увеличить(Число1, Знач Число2)
	Число1 = Число1 + 1;
	Число2 = Число2 + 1;
КонецПроцедуры

А = 5;
Б = 5;
Увеличить(А, Б);
// А = 6, Б = 5

Знач и коллекции

С массивами и структурами Знач защищает только саму переменную, а не содержимое. Процедура получает копию ссылки на ту же коллекцию:

Процедура Дополнить(Знач Список)
	Список.Добавить(1);      // исходный массив изменится
	Список = Новый Массив;   // а эта замена снаружи не видна
КонецПроцедуры

Нужна независимая копия — создайте её явно, например через Новый ФиксированныйМассив(Список) для чтения или перебором в новый массив.

Значения по умолчанию

Необязательные параметры получают значение по умолчанию в объявлении. Они идут после обязательных:

Функция СуммаСНДС(Сумма, Ставка = 20)
	Возврат Окр(Сумма * (100 + Ставка) / 100, 2);
КонецФункции

СуммаСНДС(1000);       // 1200
СуммаСНДС(1000, 10);   // 1100

В серверных методах, которые вызываются с клиента, ставьте Знач у параметров, которые не нужно возвращать. Платформа тогда не передаёт их обратно на клиент, и вызов становится легче.

Коллекции: Массив, Структура, Соответствие и таблицы

Когда одного значения мало, переменная хранит коллекцию. Выбор зависит от того, как вы будете искать данные.

КоллекцияДоступКогда брать
МассивПо индексу с 0Список однотипных значений
СтруктураПо имени ключа через точкуНабор именованных параметров
СоответствиеПо любому ключу, в том числе ссылкеСловарь «товар → цена»
СписокЗначенийПо индексу, есть пометки и представленияВыбор из списка в интерфейсе
ТаблицаЗначенийСтроки и колонки, поиск, свёрткаДанные из запроса, расчёты
ДеревоЗначенийИерархия строкГруппы и подчинённые элементы

Примеры

Товары = Новый Массив;
Товары.Добавить("Бумага");
Товары.Добавить("Тонер");
Первый = Товары[0];                 // "Бумага"
Всего = Товары.Количество();        // 2

Параметры = Новый Структура("Организация, Дата", Организация, ТекущаяДата());
Параметры.Вставить("Склад", Склад);

Цены = Новый Соответствие;
Цены.Вставить(Товар, 350);
Цена = Цены[Товар];                 // 350 или Неопределено, если ключа нет

Таблица = Новый ТаблицаЗначений;
Таблица.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число"));

Структура и Соответствие ведут себя по-разному при отсутствии ключа

  • 🔸 Цены[Товар] для отсутствующего ключа спокойно вернёт Неопределено.
  • 🔸 Параметры.Склад для отсутствующего ключа вызовет ошибку Поле объекта не обнаружено (Склад).

Безопасный способ прочитать структуру — метод Свойство():

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

Фиксированные коллекции

ФиксированныйМассив, ФиксированнаяСтруктура и ФиксированноеСоответствие нельзя изменить после создания. Их удобно возвращать из функций с повторным использованием: вызывающий код не испортит кэш.

Частые ошибки с переменными и как их избежать

Диагностика: выберите симптом

Ещё два правила, которые снимают половину отладки:

  1. Инициализируйте счётчики и суммы явно перед циклом: Итого = 0;.
  2. Не используйте одну переменную под разные смыслы. Пусть Значение не будет сначала суммой, а потом строкой отчёта.

Частые вопросы

Можно ли в 1С объявить переменную с фиксированным типом?
Нет, встроенный язык не поддерживает типизированные переменные. Тип ограничивают у реквизитов метаданных и колонок таблиц через ОписаниеТипов. В коде значение приводят методом ПривестиЗначение объекта ОписаниеТипов.
Есть ли в языке 1С константы как в других языках?
Ключевого слова для неизменяемой переменной нет. Объект метаданных «Константа» — это не переменная, а значение, которое хранится в базе и общее для всех пользователей. Для неизменяемого набора значений в коде используйте ФиксированнаяСтруктура.
Нужно ли писать Перем перед каждой локальной переменной?
Нет, первое присваивание создаёт локальную переменную. Перем внутри процедуры нужен редко: когда переменную надо объявить со значением Неопределено до условной ветки.
Как передать значение из одной формы в другую?
Через параметры формы при открытии и через оповещение или результат закрытия при возврате. Глобальная переменная модуля управляемого приложения тоже сработает, но такой обмен трудно отследить, и в типовых конфигурациях его избегают.
Почему сравнение двух структур с одинаковыми ключами даёт Ложь?
Структуры, массивы и таблицы сравниваются как объекты, а не по содержимому. Равенство истинно, только если обе переменные указывают на одну и ту же коллекцию. Содержимое сравнивают перебором.