ТЕОРИЯ И ПРАКТИЧЕСКИЕ ПРИМЕНЕНИЯ КАЛАМБУРОВ ТИПИЗАЦИИ И.А. Кучма Новосибирский государственный технический университет, г. Новосибирск, mistersandsnake@gmail.com Научный руководитель: Кобылянский В.Г., к.т.н., доцент Данная статья посвящена рассмотрению метода каламбуров типизации, анализу достоинств, недостатков и иных особенностей различных способов реализации данного метода, а также изучению пользы от его практического применения. This article is devoted to examining the method of type punning, analyzing the advantages, disadvantages and other features of various ways to implement this method, as well as researching the benefits of using it in practice. Типобезопасность, несомненно, имеет большие достоинства для программистов, предохраняя их программы от неопределенного поведения, обеспечивая защиту памяти от переполнения буфера, а также предотвращая логические ошибки. Тем не менее, существует определенный круг задач, решению которых типобезопасность не помогает, а препятствует. Некоторые задачи, входящие в этот круг, требуют осуществления определенных операций с видами данных, для этих операций не предназначенных, что является прямым нарушением типобезопасности. Тем не менее, определенные языки программирования позволяют обходить данные ограничения, интерпретируя данные одного как данные другого вида, допускающего выполнение необходимой операции. Методы, которые позволяют осуществить такой обход системы типов языка программирования, обозначаются термином каламбуры типизации. Понятие каламбуров типизации тесно связано с языками программирования со слабой типизацией, однако они осуществимы и в некоторых языках с сильной системой типов. В действительности, для реализации каламбура типизации требуется лишь наличие в языке программирования средств, позволяющих интерпретировать данные какого-то одного типа (например, число с плавающей точкой) как данные другого типа (целое число) без изменения представления данных в памяти, либо средств, позволяющих работать с памятью напрямую. Интерпретация данных как данных другого типа без изменения их внутреннего представления позволяет сделать каламбуры типизации относительно типобезопасными, наложив на них определенные ограничения. Часто смену интерпретации делают возможной лишь для простых типов данных, поскольку их размер в памяти строго определен стандартом языка. Этого достаточно для большей части прикладных задач, однако определенные задачи, связанные со структурой хранения более сложных типов данных, чаще всего невозможно решить, оставаясь в рамках этих ограничений. Примером подобных задач является задача получения индекса блока синхронизации и указателя на таблицу методов объекта в языке C#. Средства для работы с памятью напрямую позволяют различными способами сразу считывать информацию из памяти, выделенной под данные одного вида, словно это данные другого вида, что более производительно, чем сначала считывать эту информацию как данные первого типа и записывать её в память, выделенную под данные второго типа. Кроме того, это позволяет осуществлять каламбуры типизации со сложными типами данных, что полезно, например, в задаче получения внутреннего представления какого-либо объекта в языках объектноориентированного программирования, позволяющих работать с памятью напрямую. Однако эти средства при некорректном использовании представляют большую опасность для безопасности памяти. Некоторые примеры методов реализации каламбуров типизации представлены в таблице 1. Таблица 1 - Различные методы реализации каламбуров типизации Метод Преобразование типа указателя на область памяти с нужными данными Примеры языков, поддерживающих метод C/C++, C# Особенности Изначальный тип данных и получаемый тип должны иметь одинаковый размер, иначе будет нарушена безопасность памяти. Использование структурыобъединения C/C++, C# В C# требует создания структуры с макетом Explicit и определения нескольких полей с одинаковым значением FieldOffset. Использование записи для интерпретации данных более чем одним способом Pascal Поддерживает в том числе интерпретации, не предусмотренные языком Pascal. Копирование содержимого одной области памяти в другую C/C++ (memcpy), C# (Buffer.MemoryCopy) Требуется знать размер изначального типа данных. Стандартная функция memcpy не проверяет размер выходного буфера, что может приводить к ошибкам. reinterpret_cast C++ Входной и выходной типы данных должны быть перечислимыми. bit_cast C++ (начиная с C++20) В отличие от reinterpret_cast, вычисляется при компиляции. Каламбуры типизации имеют применение в достаточно широком круге задач, в первую очередь низкоуровневых задач и задач, связанных с представлением данных в компьютерной памяти. Наиболее часто применяемым в практике случаем каламбуров типизации является интерпретация чисел с плавающей точкой как целых чисел. Причин тому несколько. Во-первых, числа с плавающей точкой не поддерживают битовые операции, в то время как целые числа поддерживают их. Во-вторых, определенные операции работают гораздо быстрее или точнее с целыми числами, чем с числами с плавающей точкой. Примером таких операций служат операции сравнения. В ситуациях, когда подобные операции являются ресурсоёмкими, каламбуры типизации часто помогают оптимизировать алгоритм решения задачи. Каламбуры типизации уже сыграли свою роль в решении определенных практических задач. Ниже представлены некоторые примеры их применения. • Алгоритм нахождения быстрого обратного квадратного корня, в свое время имевший большое значение для трёхмерной компьютерной графики, имеет два использования каламбуров типизации в своём оригинальном варианте[1]. Число с плавающей точкой реинтерпретируется как целое число, а потом, после определенных вычислений, результат реинтерпретируется обратно в число с плавающей точкой. • Одна из адаптаций поразрядной сортировки для чисел с плавающей точкой включает в себя их реинтерпретацию в целые числа с последующим преобразованием их в форму, которая позволяет быстро и точно сравнивать их друг с другом[2]. • Примером использования каламбуров типизации в случае со сложными объектами служит интерфейс сокетов Беркли для межпроцессорного взаимодействия в языке C[3]. В данном API широко используется метод преобразования указателя на структуру типа sockaddr_in в указатель на структуру типа sockaddr, что является возможным благодаря их одинаковому устройству в памяти. Таким образом достигается рудиментарная форма наследования. Несмотря на то, что все эти примеры относятся к временному промежутку от 1980-х до ранних 2000-х годов, факт того, что один из методов реализации каламбуров типизации - bit_cast - был добавлен в стандарт языка C++ относительно недавно, в 2020 году, можно судить о том, что каламбуры типизации остаются актуальными и по сей день. Литература: 1. Hansen C.P. 0x5f3759df [Electronic resource] // Hummus and Magnets – URL: http://h14s.p5r.org/2012/09/0x5f3759df.html (accessed: 10.11.2022) 2. Herf M. Radix Tricks [Electronic resource] // stereopsis – 2001, December. – URL: http://stereopsis.com/radix.html (accessed: 11.11.2022) 3. bind(2) - Linux man page [Electronic resource] // die.net – URL: https://linux.die.net/man/2/bind (accessed: 11.11.2022)