Введение

Объект, хранящий адреса памяти в компьютерной программе.

В информатике указатель — это объект во многих языках программирования, который хранит адрес памяти. Это может быть адрес другого значения, расположенного в компьютерной памяти, или, в некоторых случаях, адрес аппаратного обеспечения компьютера, отображенного в память. Указатель ссылается на местоположение в памяти, и получение значения, хранящегося в этом месте, называется разыменованием указателя. В качестве аналогии, номер страницы в указателе книги можно рассматривать как указатель на соответствующую страницу; разыменование такого указателя выполняется путем перехода на страницу с указанным номером и чтения текста на этой странице. Фактический формат и содержимое переменной-указателя зависят от базовой архитектуры компьютера. Использование указателей значительно повышает производительность при повторяющихся операциях, таких как обход итерируемых структур данных (например, строк, таблиц поиска, таблиц управления и деревьев). В частности, копирование и разыменование указателей часто обходится дешевле по времени и занимаемому месту, чем копирование и доступ к данным, на которые указывают эти указатели. Указатели также используются для хранения адресов точек входа для вызываемых подпрограмм в процедурном программировании и для динамической компоновки библиотек (DLL). В объектно-ориентированном программировании указатели на функции используются для привязки методов, часто с использованием виртуальных таблиц методов. Указатель — это простая, более конкретная реализация более абстрактного типа данных — ссылки. Многие языки, особенно языки низкого уровня, поддерживают тот или иной тип указателей, хотя некоторые из них имеют больше ограничений на их использование, чем другие. Хотя термин «указатель» иногда используется для обозначения ссылок в целом, он более корректно применяется к структурам данных, интерфейс которых явно позволяет манипулировать указателем (арифметически) как адресом памяти, в отличие от «магического файла cookie» или «возможности», которые не допускают таких манипуляций. Поскольку указатели позволяют как защищенный, так и незащищенный доступ к адресам памяти, их использование сопряжено с определенными рисками, особенно в последнем случае. Примитивные указатели часто хранятся в формате, аналогичном целому числу; однако попытка разыменовать или «просмотреть» такой указатель, значение которого не является допустимым адресом памяти, может привести к сбою программы (или к содержанию неверных данных). Чтобы смягчить эту потенциальную проблему, с точки зрения типобезопасности, указатели рассматриваются как отдельный тип, параметризованный типом данных, на которые они указывают, даже если базовое представление является целым числом. Также могут быть приняты другие меры (например, проверка достоверности и границ), чтобы убедиться, что переменная-указатель содержит значение, которое является одновременно допустимым адресом памяти и находится в числовом диапазоне, который процессор способен адресовать.

История

В 1955 году советский украинский учёный-компьютерщик Екатерина Ющенко изобрела язык Address, который обеспечивал косвенное обращение к памяти и адресацию высшего порядка – аналогичную указателям. Этот язык широко использовался на компьютерах Советского Союза. Однако за пределами Советского Союза он оставался неизвестным, и изобретение указателей обычно приписывают Гарольду Лоусону, который представил их в 1964 году. В 2000 году Лоусон был удостоен награды Computer Pioneer Award от IEEE «за изобретение переменной-указателя и внедрение этой концепции в PL/I, что впервые позволило гибко работать со связными списками в языке программирования высокого уровня общего назначения». Его основополагающая работа по этим концепциям была опубликована в июньском выпуске журнала CACM в 1967 году под названием: PL/I List Processing. Согласно Оксфордскому словарю английского языка, слово "pointer" впервые появилось в печати в качестве "указателя стека" в техническом меморандуме корпорации System Development Corporation.

Официальное описание

В информатике указатель – это разновидность ссылки. Примитив данных (или просто примитив) – это любой элемент данных, который можно прочитать из или записать в компьютерную память с помощью одного обращения к памяти (например, и байт, и слово являются примитивами). Агрегат данных (или просто агрегат) – это группа примитивов, логически расположенных последовательно в памяти и рассматриваемых как единый элемент данных (например, агрегат может состоять из 3 логически соседних байтов, значения которых представляют 3 координаты точки в пространстве). Если агрегат полностью состоит из примитивов одного типа, такой агрегат может называться массивом; в определенном смысле, многобайтовое слово-примитив – это массив байтов, и некоторые программы используют слова таким образом. Указатель – это концепция программирования, используемая в информатике для ссылки или указания на место в памяти, где хранится значение или объект. По сути, это переменная, которая хранит адрес памяти другой переменной или структуры данных, а не сами данные. Указатели обычно используются в языках программирования, поддерживающих непосредственную манипуляцию памятью, таких как C и C++. Они позволяют программистам напрямую работать с памятью, обеспечивая эффективное управление памятью и более сложные структуры данных. С помощью указателей можно получать доступ и изменять данные, расположенные в памяти, эффективно передавать данные между функциями и создавать динамические структуры данных, такие как связные списки, деревья и графы. Проще говоря, указатель можно представить как стрелку, указывающую на конкретное место в памяти компьютера, позволяя взаимодействовать с данными, хранящимися в этом месте. Указатель памяти (или просто указатель) – это примитив, значение которого предназначено для использования в качестве адреса памяти; говорят, что указатель указывает на адрес памяти. Также говорят, что указатель указывает на элемент данных [в памяти], когда значение указателя является адресом памяти этого элемента данных. В более общем смысле, указатель – это разновидность ссылки, и говорят, что указатель ссылается на элемент данных, хранящийся где-то в памяти; для получения этого элемента данных необходимо выполнить разыменование указателя. Отличительной особенностью указателей от других видов ссылок является то, что значение указателя должно интерпретироваться как адрес памяти, что является концепцией довольно низкого уровня. Ссылки обеспечивают уровень косвенности: значение указателя определяет, какой адрес памяти (то есть, какой элемент данных) следует использовать в вычислении. Поскольку косвенность является фундаментальным аспектом алгоритмов, указатели часто представляются как фундаментальный тип данных в языках программирования; в статически (или строго) типизированных языках программирования тип указателя определяет тип элемента данных, на который он указывает.

Архитектурные корни

Указатели — это очень тонкая абстракция, основанная на возможностях адресации, предоставляемых большинством современных архитектур. В простейшей схеме каждой единице памяти в системе присваивается адрес, или числовой индекс, где единицей обычно является байт или слово — в зависимости от того, является ли архитектура байтно- или словно-адресуемой, — что эффективно преобразует всю память в очень большой массив. Система также предоставляет операцию для извлечения значения, хранящегося в единице памяти по заданному адресу (обычно с использованием регистров общего назначения процессора). Как правило, указатель достаточно велик, чтобы содержать больше адресов, чем единиц памяти в системе. Это создает возможность того, что программа может попытаться обратиться к адресу, который не соответствует ни одной единице памяти, либо из-за недостаточного объема установленной памяти (т. е. за пределами диапазона доступной памяти), либо из-за того, что архитектура не поддерживает такие адреса. В первом случае, на некоторых платформах, таких как архитектура Intel x86, это может называться ошибкой сегментации (segfault). Второй случай возможен в текущей реализации AMD64, где указатели имеют длину 64 бита, а адреса ограничены 48 битами. Указатели должны соответствовать определенным правилам (канонические адреса), поэтому при разыменовании неканонического указателя процессор генерирует общее нарушение защиты. С другой стороны, в некоторых системах количество единиц памяти превышает количество адресов. В этом случае для использования различных частей памяти в разное время используется более сложная схема, такая как сегментация памяти или страничная организация. Последние версии архитектуры x86 поддерживают до 36 бит физических адресов памяти, которые отображаются в 32-битное линейное адресное пространство с помощью механизма страничного отображения PAE. Таким образом, одновременно доступна только 1/16 от общего объема возможной памяти. Другим примером в том же семействе компьютеров был 16-битный защищенный режим процессора 80286, который, хотя и поддерживал только 16 МБ физической памяти, мог обращаться до 1 ГБ виртуальной памяти, но комбинация 16-битных адресов и сегментных регистров затрудняла доступ к более чем 64 КБ в одной структуре данных. Для обеспечения согласованного интерфейса некоторые архитектуры предоставляют ввод-вывод с отображением в память, что позволяет некоторым адресам ссылаться на единицы памяти, а другим — на регистры устройств других устройств в компьютере. Существуют аналогичные концепции, такие как смещения файлов, индексы массивов и ссылки на удаленные объекты, которые выполняют некоторые из тех же функций, что и адреса для других типов объектов.

Применение

Указатели поддерживаются напрямую и без ограничений в таких языках, как PL/I, C, C++, Pascal, FreeBASIC, и неявно – в большинстве языков ассемблера. Они в основном используются для создания ссылок, которые, в свою очередь, являются основой для построения практически всех структур данных, а также для передачи данных между различными частями программы. В функциональных языках программирования, активно использующих списки, ссылки на данные управляются абстрактно с помощью примитивных конструкций, таких как `cons` и соответствующие элементы `car` и `cdr`, которые можно рассматривать как специализированные указатели на первый и второй компоненты `cons`-ячейки. Это придает функциональному программированию своеобразный идиоматический стиль. Структурируя данные в виде таких `cons`-списков, эти языки облегчают рекурсивные методы создания и обработки данных, например, рекурсивный доступ к головным и хвостовым элементам списков списков; например, "получение `car` от `cdr` от `cdr`". В отличие от этого, управление памятью, основанное на разыменовании указателей в некотором приближении массива адресов памяти, позволяет рассматривать переменные как слоты, в которые данные могут быть присвоены императивно. При работе с массивами критическая операция поиска обычно включает этап, называемый вычислением адреса, который заключается в построении указателя на нужный элемент данных в массиве. В других структурах данных, таких как связные списки, указатели используются как ссылки для явного связывания частей структуры друг с другом. Указатели используются для передачи параметров по ссылке. Это полезно, если программисту необходимо, чтобы изменения, внесенные функцией в параметр, были видны вызывающей функции. Это также полезно для возврата нескольких значений из функции. Указатели также могут использоваться для выделения и освобождения динамических переменных и массивов в памяти. Поскольку переменная часто становится ненужной после выполнения своей задачи, хранить ее – это пустая трата памяти, поэтому хорошей практикой является ее освобождение (с использованием исходной ссылки-указателя) после того, как она больше не нужна. Несоблюдение этого требования может привести к утечке памяти (когда объем доступной свободной памяти постепенно или, в серьезных случаях, быстро уменьшается из-за накопления множества избыточных блоков памяти).

Использование в структурах данных

При создании структур данных, таких как списки, очереди и деревья, необходимо использовать указатели для управления реализацией и контролем структуры. Типичными примерами указателей являются указатели начала, указатели конца и указатели стека. Эти указатели могут быть абсолютными (фактический физический адрес или виртуальный адрес в виртуальной памяти) или относительными (смещение от абсолютного начального адреса ("базы"), которое обычно требует меньше битов, чем полный адрес, но обычно нуждается в одной дополнительной арифметической операции для вычисления). Относительные адреса представляют собой форму ручной сегментации памяти и разделяют многие её преимущества и недостатки. Двухбайтовое смещение, содержащее 16-битное беззнаковое целое число, может использоваться для относительной адресации до 64 КиБ (2<sup>16</sup> байт) структуры данных. Это можно легко расширить до 128, 256 или 512 КиБ, если адрес, на который указывает смещение, должен быть выровнен по границе полуслова, слова или двойного слова (но это потребует дополнительной битовой операции сдвига влево на 1, 2 или 3 бита для корректировки смещения в 2, 4 или 8 раз, прежде чем добавить его к базовому адресу). Однако, как правило, такие схемы сложны в реализации, и для удобства программиста предпочтительны абсолютные адреса (и, как следствие, плоское адресное пространство). Однобайтовое смещение, например, шестнадцатеричное значение ASCII символа (например, X'29'), может использоваться для указания на альтернативное целочисленное значение (или индекс) в массиве (например, X'01'). Таким образом, символы можно очень эффективно преобразовывать из "сырых данных" в полезный последовательный индекс, а затем в абсолютный адрес без использования таблицы поиска.

Значение указателей

В C и C++, даже если два указателя сравниваются как равные, это не означает, что они эквивалентны. В этих языках и LLVM это правило интерпретируется как то, что "тот факт, что два указателя указывают на один и тот же адрес, не означает, что они могут использоваться взаимозаменяемо", а разница между указателями называется их происхождением. Приведение к целочисленному типу, такому как uintptr_t, зависит от реализации, и сравнение, которое оно предоставляет, не дает дополнительной информации о том, взаимозаменяемы ли два указателя. Более того, дальнейшее преобразование в байты и арифметические операции могут сбить с толку оптимизаторы, пытающиеся отслеживать использование указателей – проблема, которая все еще изучается в академических исследованиях.

Убедительные указатели

Поскольку указатель позволяет программе пытаться получить доступ к объекту, который может быть не определен, указатели могут быть причиной множества ошибок программирования. Однако полезность указателей настолько велика, что выполнение задач программирования без них может быть затруднительным. Следовательно, многие языки создали конструкции, предназначенные для обеспечения некоторых полезных возможностей указателей, избегая при этом их недостатков, также иногда называемых опасностями, связанными с указателями. В этом контексте указатели, которые напрямую адресуют память (как это используется в данной статье), называются «сырыми» указателями, в отличие от «умных» указателей или других вариантов. Одна из основных проблем с указателями заключается в том, что, пока они могут напрямую манипулироваться как числа, они могут указывать на неиспользуемые адреса или на данные, которые используются для других целей. Многие языки, включая большинство функциональных языков программирования и современные императивные языки, такие как Java, заменяют указатели более непрозрачным типом ссылки, обычно называемой просто ссылкой, которая может использоваться только для обращения к объектам и не может манипулироваться как число, предотвращая тем самым этот тип ошибки. Индексирование массивов рассматривается как особый случай. Указатель, которому не присвоен адрес, называется «неопределенным» указателем. Любая попытка использовать такие неинициализированные указатели может привести к непредсказуемому поведению, либо потому, что начальное значение не является допустимым адресом, либо потому, что его использование может повредить другие части программы. Результатом часто является ошибка сегментации, нарушение прав доступа к памяти или переход по недействительному адресу (если используется как указатель на функцию или адрес перехода). В системах с явным выделением памяти можно создать «висячий» указатель, освободив область памяти, на которую он указывает. Этот тип указателя опасен и незаметен, поскольку освобожденная область памяти может содержать те же данные, что и до освобождения, но затем может быть перераспределена и перезаписана несвязанным кодом, о чем не знает предыдущий код. Языки со сборкой мусора предотвращают этот тип ошибки, поскольку освобождение памяти выполняется автоматически, когда не остается ссылок на объект. Некоторые языки, такие как C++, поддерживают «умные» указатели, которые используют простую форму подсчета ссылок для отслеживания выделения динамической памяти, а также действуют как ссылки. При отсутствии циклических ссылок, когда объект косвенно ссылается на себя через последовательность «умных» указателей, они исключают возможность появления «висячих» указателей и утечек памяти. Строки в Delphi поддерживают подсчет ссылок нативно. Язык программирования Rust вводит проверщик заимствований, время жизни указателей и оптимизацию на основе типов-опций для нулевых указателей, чтобы устранить ошибки, связанные с указателями, без использования сборки мусора.

Пустота

Нулевой указатель имеет значение, зарезервированное для обозначения того, что он не указывает на допустимый объект. Нулевые указатели часто используются для представления ситуаций, таких как конец списка неизвестной длины или неудача выполнения некоторой операции; такое использование нулевых указателей можно сравнить с допускающими значение null типами и со значением Nothing в типе Option.

Дикая ветвь

Когда указатель используется как адрес точки входа в программу или начала функции, которая ничего не возвращает и при этом не был инициализирован или поврежден, и если, несмотря на это, осуществляется вызов или переход по этому адресу, говорят, что произошел "неконтролируемый переход". Иными словами, неконтролируемый переход – это "висячий" указатель на функцию. Последствия обычно непредсказуемы, и ошибка может проявляться различными способами в зависимости от того, является ли указатель "валидным" адресом и присутствует ли (случайно) валидная инструкция (опкод) по этому адресу. Обнаружение неконтролируемого перехода может быть одним из самых сложных и изнурительных этапов отладки, поскольку значительная часть информации об ошибке может быть уже уничтожена до начала отладки или выполнением одной или нескольких некорректных инструкций в точке перехода. Если он доступен, эмулятор набора команд обычно может не только обнаружить неконтролируемый переход до его выполнения, но и предоставить полную или частичную трассировку его истории.

Направленный указатель

Основанный указатель — это указатель, значение которого является смещением от значения другого указателя. Это позволяет хранить и загружать блоки данных, присваивая адрес начала блока базовому указателю.

Задний указатель

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

Моделирование с использованием индекса массива

Можно имитировать поведение указателя, используя индекс в (обычно одномерном) массиве. Прежде всего, это полезно для языков, которые явно не поддерживают указатели, но поддерживают массивы. Такой массив можно рассматривать и обрабатывать как весь диапазон памяти (в пределах конкретного массива), а любой индекс к нему можно считать эквивалентным регистру общего назначения в языке ассемблера (который указывает на отдельные байты, но его фактическое значение относительно начала массива, а не абсолютного адреса в памяти). Если, например, массив представляет собой непрерывную структуру данных размером 16 мегабайт, отдельные байты (или последовательность смежных байтов в массиве) можно напрямую адресовать и изменять, используя имя массива и 31-битное беззнаковое целое число в качестве имитированного указателя (это очень похоже на пример массивов в C, приведенный выше). Арифметические операции с указателями можно имитировать, добавляя или вычитая значения из индекса, с минимальными дополнительными накладными расходами по сравнению с настоящей арифметикой указателей. Теоретически, используя описанный метод в сочетании с подходящим эмулятором набора инструкций, можно даже имитировать любой машинный код или промежуточный (байтовый код) любого процессора/языка в другом языке, который вообще не поддерживает указатели (например, Java / JavaScript). Для этого двоичный код можно сначала загрузить в смежные байты массива, чтобы эмулятор мог "читать", интерпретировать и выполнять его, полностью в памяти, содержащейся в этом массиве. При необходимости, для полного предотвращения проблем с переполнением буфера, проверку границ обычно можно включить в компилятор (или, если это невозможно, реализовать вручную в эмуляторе).

Эда

Ada — это язык со строгой типизацией, где все указатели имеют тип, и разрешены только безопасные преобразования типов. Все указатели по умолчанию инициализируются значением null, и любая попытка доступа к данным через null-указатель приводит к возникновению исключения. Указатели в Ada называются типами доступа. В Ada 83 арифметические операции над типами доступа не допускались (хотя многие производители компиляторов предоставляли такую возможность как нестандартную функцию), но Ada 95 поддерживает "безопасную" арифметику над типами доступа посредством пакета System.Storage_Elements.

Кобол

Язык программирования COBOL поддерживает указатели на переменные. Примитивные или групповые (структурные) объекты данных, объявленные в секции LINKAGE, по своей сути основаны на указателях, при этом в программе выделяется память только для адреса элемента данных (обычно одно машинное слово). В исходном коде программы эти элементы данных используются как любые другие переменные WORKING STORAGE, но к их содержимому осуществляется неявный косвенный доступ через указатели LINKAGE. Память для каждого объекта данных, на который указывают, обычно выделяется динамически с помощью внешних вызовов CALL или встроенных расширенных конструкций языка, таких как EXEC CICS или EXEC SQL. Расширенные версии COBOL также предоставляют переменные-указатели, объявленные с помощью пункта USAGE IS POINTER. Значения таких переменных-указателей устанавливаются и изменяются с помощью операторов SET и SET ADDRESS. Некоторые расширенные версии COBOL также предоставляют переменные PROCEDURE POINTER, способные хранить адреса исполняемого кода.

ПЛ/И

Язык PL/I обеспечивает полную поддержку указателей на все типы данных (включая указатели на структуры), рекурсию, многозадачность, обработку строк и обширные встроенные функции. PL/I был значительным шагом вперед по сравнению с языками программирования того времени. Указатели PL/I не имеют типа, поэтому при разыменовании или присваивании указателя приведение типов не требуется. Синтаксис объявления указателя: DECLARE xxx POINTER;, что объявляет указатель с именем "xxx". Указатели используются с переменными, объявленными с атрибутом BASED. Переменная с атрибутом BASED может быть объявлена с указанием локатора по умолчанию (DECLARE xxx BASED(ppp);) или без него (DECLARE xxx BASED;), где xxx – переменная с атрибутом BASED, которая может быть элементом, структурой или массивом, а ppp – указатель по умолчанию. К такой переменной можно обращаться без явной ссылки на указатель (xxx=1;), либо с явной ссылкой на локатор по умолчанию (ppp), либо на любой другой указатель (qqq >xxx=1;). Арифметика указателей не является частью стандарта PL/I, но многие компиляторы допускают выражения вида ptr = ptr±expression. В IBM PL/I также имеется встроенная функция PTRADD для выполнения арифметических операций с указателями. Арифметика указателей всегда выполняется в байтах. Компиляторы IBM Enterprise PL/I имеют новую форму типизированного указателя, называемую HANDLE.

Д.

Язык программирования D является производным от C и C++, полностью поддерживающим указатели C и приведение типов C.

Эйфель

В объектно-ориентированном языке Эйфель используются семантика значения и семантика ссылок без арифметики указателей. Тем не менее, предусмотрены классы указателей. Они обеспечивают арифметику указателей, приведение типов, явное управление памятью, взаимодействие с программным обеспечением, написанным не на Эйфеле, и другие возможности.

Иди .

У Go есть указатели. Синтаксис их объявления эквивалентен синтаксису C, но записан в обратном порядке, с указанием типа в конце. В отличие от C, в Go реализован сборщик мусора и запрещена указательная арифметика. Типы ссылок, как в C++, отсутствуют. Некоторые встроенные типы, такие как отображения (maps) и каналы, являются "коробчатыми" (то есть, внутри они представляют собой указатели на изменяемые структуры) и инициализируются с помощью функции `make`. В стремлении к унифицированному синтаксису для указателей и не-указателей, оператор стрелки (>) был упразднён: оператор точки, примененный к указателю, обращается к полю или методу разыменованного объекта. Однако это работает только с одним уровнем косвенности.

Ява

В Java нет явного представления указателей. Вместо этого более сложные структуры данных, такие как объекты и массивы, реализуются с использованием ссылок. Язык не предоставляет никаких операторов для явного манипулирования указателями. Тем не менее, код может попытаться обратиться к значению по нулевой ссылке (null pointer), что приведет к возникновению исключения во время выполнения. Память, занимаемая неиспользуемыми объектами, автоматически освобождается сборщиком мусора во время выполнения.

Модуль-2

Указатели реализованы очень похоже на то, как это сделано в Паскале, как и параметры, передаваемые по ссылке (VAR) при вызовах процедур. Modula 2 имеет еще более строгую типизацию, чем Pascal, и предлагает меньше возможностей для обхода системы типов. Некоторые варианты Modula 2 (например, Modula 3) включают автоматическую сборку мусора.

Оберон

Как и в Modula 2, предусмотрены указатели. Однако существует меньше способов обойти систему типов, поэтому Oberon и его варианты более безопасны в отношении работы с указателями, чем Modula 2 или его варианты. Как и в Modula 3, сборка мусора входит в спецификацию языка.

Паскаль

В отличие от многих языков, использующих указатели, стандартный ISO Pascal позволяет указателям ссылаться только на динамически созданные анонимные переменные и не разрешает им ссылаться на стандартные статические или локальные переменные. Арифметические операции с указателями в нём не предусмотрены. Указатели должны иметь ассоциированный тип, и указатель одного типа несовместим с указателем другого типа (например, указатель на символ несовместим с указателем на целое число). Это помогает избежать проблем с типобезопасностью, характерных для других реализаций указателей, особенно используемых в PL/I или C. Это также снижает некоторые риски, связанные с висячими указателями, однако возможность динамического освобождения занимаемой памяти с помощью стандартной процедуры `dispose` (которая эквивалентна библиотечной функции `free` в C) не исключает полностью возможность появления висячих указателей. Тем не менее, в некоторых коммерческих и открытых компиляторах Pascal (или их производных) – таких как Free Pascal, Turbo Pascal или Object Pascal в Embarcadero Delphi – указатели могут ссылаться на стандартные статические или локальные переменные и могут быть преобразованы из одного типа указателя в другой. Более того, арифметические операции с указателями не ограничены: добавление или вычитание от указателя перемещает его на указанное количество байт в любом направлении, но использование стандартных процедур `Inc` или `Dec` перемещает указатель на размер типа данных, на который он указывает. Также предусмотрен нетипизированный указатель с именем `Pointer`, который совместим с другими типами указателей.

Перл

Язык программирования Perl поддерживает указатели, хотя и редко используемые, в виде функций `pack` и `unpack`. Они предназначены исключительно для простого взаимодействия с скомпилированными библиотеками операционной системы. Во всех остальных случаях Perl использует ссылки, которые имеют тип и не допускают никаких операций с арифметикой указателей. Они используются для создания сложных структур данных.