Обмен значениями двух переменных — классическая задача, с которой сталкивается каждый разработчик 1С. В большинстве случаев для этого используется временная переменная, но что делать, если по каким-то причинам её применение невозможно или нежелательно? Например, при работе с ограниченной памятью, в низкоуровневых процедурах или при оптимизации кода под специфические задачи.

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

Особое внимание уделено типовым ошибкам, которые возникают при обмене переменных без буфера, — от потери данных до некорректной работы с Неопределённым значением. В конце статьи вы найдёте сравнительную таблицу методов и FAQ с ответами на частые вопросы.

📊 Какой способ обмена переменными вы используете чаще?
Через временную переменную
Арифметические операции
Побитовый XOR
Массивы или структуры
Другие методы

1. Арифметический метод: сложение и вычитание

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

  1. Сложить значения переменных и сохранить результат в первой переменной.
  2. Вычесть из суммы значение второй переменной — получим исходное значение первой.
  3. Вычесть из суммы новое значение первой переменной — получим исходное значение второй.

Пример кода на 1С:

Перем А, Б;

А = 10;

Б = 20;

// Обмен значениями

А = А + Б; // А = 30 (10 + 20)

Б = А - Б; // Б = 10 (30 - 20)

А = А - Б; // А = 20 (30 - 10)

Сообщить("А = " + А + ", Б = " + Б); // Выведет: А = 20, Б = 10

Плюсы:

  • 🔢 Простота реализации — достаточно трёх строк кода.
  • 📊 Подходит для любых числовых типов (Число, Дробь).

Минусы:

  • ⚠️ Переполнение — если сумма переменных превышает максимальное значение типа, результат будет некорректным.
  • 🚫 Не работает со строками, датами, объектами 1С.
⚠️ Внимание: При обмене переменных типа Число(10,2) убедитесь, что сумма не превышает 999 999 999.99, иначе произойдёт автоматическое округление или ошибка.
💡

Для проверки переполнения перед обменом используйте функцию ТипЗнчМаксимум() или сравните сумму с константой Максимум(Тип("Число")).

2. Побитовый XOR: обмен без риска переполнения

Метод с использованием операции ПобитовоеИсключающееИли() (XOR) подходит для обмена целыми числами. Его преимущество — отсутствие риска переполнения, так как операции выполняются на уровне битов. Алгоритм:

  1. Применяем XOR к обеим переменным и сохраняем результат в первой.
  2. Применяем XOR к новой первой переменной и второй — получаем исходное значение первой.
  3. Применяем XOR к новой первой переменной и новому значению второй — получаем исходное значение второй.

Пример кода:

Перем А, Б;

А = 5; // Двоичное: 0101

Б = 3; // Двоичное: 0011

// Обмен значениями

А = ПобитовоеИсключающееИли(А, Б); // А = 6 (0110)

Б = ПобитовоеИсключающееИли(А, Б); // Б = 5 (0101)

А = ПобитовоеИсключающееИли(А, Б); // А = 3 (0011)

Сообщить("А = " + А + ", Б = " + Б); // Выведет: А = 3, Б = 5

Преимущества:

  • 🔒 Безопасен от переполнения — работает на уровне битов.
  • 🚀 Быстрее арифметического метода для больших чисел.

Ограничения:

  • 🔢 Работает только с целыми числами (Число(10,0)).
  • 🛑 Неприменим к строкам, датам, справочникам.
⚠️ Внимание: В 1С функция ПобитовоеИсключающееИли() возвращает число типа Число(10,0). Если ваши переменные имеют другой тип (например, Число(15,0)), приведите их явно перед обменом.
Что произойдёт, если применить XOR к отрицательным числам?

Побитовый XOR корректно работает с отрицательными числами, так как в 1С они хранятся в дополнительном коде. Например, для А = -5 (двоичное: 1011) и Б = -3 (двоичное: 1101) обмен пройдёт без ошибок. Однако визуально результат может казаться нелогичным из-за особенностей хранения знаковых чисел.

3. Обмен с использованием массивов или структур

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

Пример с массивом:

Перем А, Б;

А = "Привет";

Б = "Мир";

// Создаём массив и копируем значения

МассивВременный = Новый Массив;

МассивВременный.Добавить(А);

МассивВременный.Добавить(Б);

// Обмен значениями

А = МассивВременный[1];

Б = МассивВременный[0];

Сообщить("А = " + А + ", Б = " + Б); // Выведет: А = Мир, Б = Привет

Плюсы:

  • 🌍 Универсальность — работает с любыми типами данных.
  • 🔄 Подходит для обмена более чем двух переменных.

Минусы:

  • 🗑️ Требует создания дополнительного объекта (массива/структуры).
  • 🐢 Медленнее, чем арифметические или побитовые операции.

Альтернативный вариант — использование Структуры:

СтруктураВременная = Новый Структура("А,Б", А, Б);

А = СтруктураВременная.Б;

Б = СтруктураВременная.А;

Убедиться, что переменные не являются Неопределённым значением

Проверить, что массив не используется в другом потоке

Очистить массив после обмена, если он больше не нужен-->

4. Обмен через конкатенацию строк

Для строковых переменных можно использовать конкатенацию с разделителем. Метод аналогичен арифметическому, но вместо сложения применяется объединение строк с уникальным символом-разделителем.

Пример:

Перем Строка1, Строка2;

Строка1 = "Hello";

Строка2 = "World";

// Обмен значениями

Строка1 = Строка1 + "|" + Строка2; // "Hello|World"

Строка2 = Лев(Строка1, Найти(Строка1, "|") - 1); // "Hello"

Строка1 = Сред(Строка1, Найти(Строка1, "|") + 1); // "World"

Сообщить("Строка1 = " + Строка1 + ", Строка2 = " + Строка2);

Когда уместно:

  • 📜 Только для строковых переменных.
  • 🔍 Разделитель должен гарантированно отсутствовать в исходных строках.

Риски:

  • 💥 Ошибка, если разделитель (|) встречается в одной из строк.
  • 🐌 Низкая производительность для длинных строк.
⚠️ Внимание: Если строки могут содержать разделитель, используйте Символы.ПС (непечатаемый символ) или генерацию уникального разделителя через УникальныйИдентификатор().

5. Обмен с использованием встроенных функций 1С

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

Пример:

Перем Объект1, Объект2;

// Создаём уникальные ключи

Ключ1 = ПоместитьВоВременноеХранилище(Объект1);

Ключ2 = ПоместитьВоВременноеХранилище(Объект2);

// Обмен значениями

Объект1 = ПолучитьИзВременногоХранилища(Ключ2);

Объект2 = ПолучитьИзВременногоХранилища(Ключ1);

Плюсы:

  • 🏢 Работает с объектами 1С (справочники, документы, планы обмена).
  • 🔐 Безопасно в многопользовательском режиме.

Минусы:

  • 🗃️ Требует очистки временного хранилища после обмена.
  • 🚫 Не подходит для простых типов (Число, Строка).

Альтернатива — использование СериализацияXDTO для объектов:

ЗаписьXDTO = Новый ЗаписьXDTO;

ЗаписьXDTO.ЗаписатьXML(Объект1);

// ... обмен через сериализованные данные

ЧтениеXDTO = Новый ЧтениеXDTO;

Объект1 = ЧтениеXDTO.ПрочитатьXML();

Сравнительная таблица методов обмена переменных

Метод Типы данных Скорость Риски Пример использования
Арифметический Числа, дробь ⚡ Быстро Переполнение Обмен счетчиков в отчётах
Побитовый XOR Целые числа ⚡⚡ Очень быстро Только для целых чисел Оптимизация циклов
Массивы/Структуры Любые 🐢 Медленно Дополнительная память Обмен объектами метаданных
Конкатенация строк Строки 🐢 Медленно Коллизии разделителей Парсинг текстовых данных
Временное хранилище Объекты 1С 🐢 Медленно Требует очистки Обмен данными между сеансами
💡

Для числовых переменных оптимален побитовый XOR — он быстр и безопасен от переполнения. Для объектов 1С лучше использовать временное хранилище или структуры.

Частые ошибки и как их избежать

Даже опытные разработчики 1С допускают ошибки при обмене переменных без буфера. Рассмотрим самые распространённые:

  1. Потеря данных при арифметическом методе:

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

    Если А + Б > Максимум(Тип("Число")) Тогда
    

    ВызватьИсключение "Переполнение при обмене переменных!";

    КонецЕсли;

  2. Работа с Неопределённым значением:

    Если одна из переменных равна Неопределено, арифметические операции или XOR приведёт к ошибке. Проверяйте перед обменом:

    Если А = Неопределено Или Б = Неопределено Тогда
    

    Возврат;

    КонецЕсли;

  3. Обмен объектов с circular references:

    Если объекты ссылаются друг на друга (например, Документ.Ссылка и Документ.Родитель), сериализация или копирование через структуру может привести к зацикливанию.

⚠️ Внимание: В платформе 1С 8.3.20+ при обмене объектов через временное хранилище может срабатывать механизм блокировок. Убедитесь, что объекты не заблокированы другими транзакциями.

FAQ: Ответы на частые вопросы

Можно ли обменять переменные типа Дата без третьей переменной?

Да, но только через конвертацию в число (количество секунд с 1900 года) или с использованием массива/структуры. Пример:

Дата1 = '2023-01-01';

Дата2 = '2023-12-31';

// Через конвертацию в число

Число1 = Дата1.ПолучитьСекундыСНачалаЭры();

Число2 = Дата2.ПолучитьСекундыСНачалаЭры();

// Обмен числовыми значениями (методом XOR или арифметическим)

...

// Возвращаем обратно в тип Дата

Дата1 = НачалоДня(Число1);

Дата2 = НачалоДня(Число2);

Как обменять элементы массива без временной переменной?

Для элементов массива удобно использовать арифметический метод или XOR, если элементы — числа. Пример для индексов i и j:

Массив[і] = Массив[і] + Массив[j];

Массив[j] = Массив[і] - Массив[j];

Массив[і] = Массив[і] - Массив[j];

Для объектов лучше использовать Вставить() и Удалить():

Массив.Вставить(j, Массив[i]);

Массив.Удалить(i + 1);

Массив.Вставить(i, Массив[j - 1]);

Массив.Удалить(j);

Почему после обмена XOR переменные стали отрицательными?

Это нормальное поведение при работе с знаковыми целыми числами. XOR не меняет знак числа, но если исходные значения имели установленный старший бит (для отрицательных чисел), результат может интерпретироваться как отрицательное число. Например:

А = -1;  // Двоичное: 1111 (в дополнительном коде)

Б = 3; // Двоичное: 0011

А = ПобитовоеИсключающееИли(А, Б); // Результат: 1100 (-4 в дополнительном коде)

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

А = ПобитовоеИсключающееИли(БитовоеНЕ(А), БитовоеНЕ(Б));
Можно ли обменять переменные в запросе 1С без временных полей?

В языке запросов 1С нет прямой поддержки обмена переменных, но можно использовать ВЫБРАТЬ с ПОМЕСТИТЬ во временную таблицу. Пример:

ВЫБРАТЬ

Значение1 КАК Зн1,

Значение2 КАК Зн2

ПОМЕСТИТЬ ВТТ

УСТАНОВИТЬ НОВЫЙ ПАРАМЕТР;

ВЫБРАТЬ

ВТТ.Зн2 КАК НовоеЗначение1,

ВТТ.Зн1 КАК НовоеЗначение2

ИЗ

ВТТ КАК ВТТ;

Однако это не настоящий обмен, а создание новой выборки с переставленными значениями.

Какой метод самый быстрый для обмена переменных в 1С?

По результатам тестов на платформе 1С 8.3.22:

  1. Побитовый XOR — быстрее всего для целых чисел (в 1.5–2 раза быстрее арифметического метода).
  2. Арифметический метод — второй по скорости для чисел.
  3. Структуры/массивы — медленнее в 5–10 раз из-за накладных расходов на создание объекта.

Для строк и объектов альтернативы временной переменной или структуре нет — они всегда будут медленнее.