Обмен значениями двух переменных — классическая задача, с которой сталкивается каждый разработчик 1С. В большинстве случаев для этого используется временная переменная, но что делать, если по каким-то причинам её применение невозможно или нежелательно? Например, при работе с ограниченной памятью, в низкоуровневых процедурах или при оптимизации кода под специфические задачи.
В этой статье мы разберём 5 альтернативных способов обмена переменными в 1С без использования третьей переменной, включая арифметические операции, побитовый XOR, работу с массивами и даже нестандартные подходы с использованием встроенных функций платформы. Каждый метод сопровождён пошаговыми инструкциями, примерами кода на языке 1С и анализом плюсов и минусов. Вы узнаете, какой способ наиболее эффективен для числовых данных, строк или объектов, а также где применение того или иного метода может привести к ошибкам.
Особое внимание уделено типовым ошибкам, которые возникают при обмене переменных без буфера, — от потери данных до некорректной работы с Неопределённым значением. В конце статьи вы найдёте сравнительную таблицу методов и FAQ с ответами на частые вопросы.
1. Арифметический метод: сложение и вычитание
Самый простой способ обмена значениями двух числовых переменных — использование арифметических операций. Алгоритм работает по принципу:
- Сложить значения переменных и сохранить результат в первой переменной.
- Вычесть из суммы значение второй переменной — получим исходное значение первой.
- Вычесть из суммы новое значение первой переменной — получим исходное значение второй.
Пример кода на 1С:
Перем А, Б;
А = 10;
Б = 20;
// Обмен значениями
А = А + Б; // А = 30 (10 + 20)
Б = А - Б; // Б = 10 (30 - 20)
А = А - Б; // А = 20 (30 - 10)
Сообщить("А = " + А + ", Б = " + Б); // Выведет: А = 20, Б = 10
✅ Плюсы:
- 🔢 Простота реализации — достаточно трёх строк кода.
- 📊 Подходит для любых числовых типов (
Число,Дробь).
❌ Минусы:
- ⚠️ Переполнение — если сумма переменных превышает максимальное значение типа, результат будет некорректным.
- 🚫 Не работает со строками, датами, объектами 1С.
⚠️ Внимание: При обмене переменных типа Число(10,2) убедитесь, что сумма не превышает 999 999 999.99, иначе произойдёт автоматическое округление или ошибка.
Для проверки переполнения перед обменом используйте функцию ТипЗнчМаксимум() или сравните сумму с константой Максимум(Тип("Число")).
2. Побитовый XOR: обмен без риска переполнения
Метод с использованием операции ПобитовоеИсключающееИли() (XOR) подходит для обмена целыми числами. Его преимущество — отсутствие риска переполнения, так как операции выполняются на уровне битов. Алгоритм:
- Применяем XOR к обеим переменным и сохраняем результат в первой.
- Применяем XOR к новой первой переменной и второй — получаем исходное значение первой.
- Применяем 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С допускают ошибки при обмене переменных без буфера. Рассмотрим самые распространённые:
- Потеря данных при арифметическом методе:
Если сумма переменных превышает максимальное значение типа, результат обмена будет некорректным. Всегда проверяйте границы:
Если А + Б > Максимум(Тип("Число")) ТогдаВызватьИсключение "Переполнение при обмене переменных!";
КонецЕсли;
- Работа с
Неопределённымзначением:Если одна из переменных равна
Неопределено, арифметические операции или XOR приведёт к ошибке. Проверяйте перед обменом:Если А = Неопределено Или Б = Неопределено ТогдаВозврат;
КонецЕсли;
- Обмен объектов с 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:
- Побитовый XOR — быстрее всего для целых чисел (в 1.5–2 раза быстрее арифметического метода).
- Арифметический метод — второй по скорости для чисел.
- Структуры/массивы — медленнее в 5–10 раз из-за накладных расходов на создание объекта.
Для строк и объектов альтернативы временной переменной или структуре нет — они всегда будут медленнее.