Указатель (компьютерное программирование) - Pointer (computer programming)

Я действительно считаю операторы присваивания а переменные-указатели станут одним из «самых ценных сокровищ информатики».

Дональд Кнут, Структурированное программирование с переходом к операторам[1]

Указатель а указывая на адрес памяти, связанный с переменной б. На этой диаграмме вычислительная архитектура использует тот же адресное пространство и примитив данных как для указателей, так и для не указателей; в этом не должно быть необходимости.

В Информатика, а указатель является объект во многих языки программирования что хранит адрес памяти. Это может быть другое значение, расположенное в память компьютера, или в некоторых случаях с отображением памяти компьютерное железо. Указатель Рекомендации место в памяти, и получение значения, хранящегося в этом месте, известно как разыменование указатель. По аналогии, номер страницы в указателе книги можно рассматривать как указатель на соответствующую страницу; разыменование такого указателя будет осуществляться путем перехода на страницу с заданным номером и чтения текста, найденного на этой странице. Фактический формат и содержимое переменной-указателя зависят от лежащего в основе компьютерная архитектура.

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

Указатели также используются для хранения адресов точек входа для называется подпрограммы в процедурное программирование и для ссылки во время выполнения на библиотеки динамической компоновки (DLL). В объектно-ориентированного программирования, указатели на функции используются для привязка методы, часто используя таблицы виртуальных методов.

Указатель - это простая и конкретная реализация более абстрактного ссылка тип данных. Несколько языков, особенно языки низкого уровня, поддерживают некоторые типы указателей, хотя некоторые имеют больше ограничений на их использование, чем другие. Хотя «указатель» использовался для обозначения ссылок в целом, он более правильно применяется к структуры данных чей интерфейс явно позволяет манипулировать указателем (арифметически через арифметика указателя) как адрес памяти, в отличие от волшебное печенье или же способность что не позволяет такое.[нужна цитата ] Поскольку указатели позволяют как защищенный, так и незащищенный доступ к адресам памяти, с их использованием связаны риски, особенно в последнем случае. Примитивные указатели часто хранятся в формате, аналогичном формату целое число; однако попытка разыменования или "поиска" такого указателя, значение которого не является допустимым адресом памяти, приведет к тому, что программа крушение. Чтобы облегчить эту потенциальную проблему, безопасность типа, указатели считаются отдельным типом, параметризованным типом данных, на которые они указывают, даже если базовое представление является целым числом. Также могут быть приняты другие меры (например, Проверка & проверка границ ), чтобы убедиться, что переменная-указатель содержит значение, которое одновременно является допустимым адресом памяти и находится в числовом диапазоне, который процессор может адресовать.

История

В 1955 году советский ученый-компьютерщик Катерина Ющенко изобрел Адресный язык программирования что сделало возможной косвенную адресацию и адреса высшего ранга - аналогично указателям. Этот язык широко использовался на компьютерах Советского Союза. Однако за пределами Советского Союза он был неизвестен и обычно Гарольд Лоусон приписывают изобретение указателя в 1964 году.[2] В 2000 году Лоусон был награжден премией Computer Pioneer Award от IEEE «[f] или изобретение переменной-указателя и внедрение этой концепции в PL / I, тем самым впервые предоставляя возможность гибко обрабатывать связанные списки на универсальном языке высокого уровня».[3] Его основополагающий документ о концепциях появился в июньском выпуске CACM за 1967 год, озаглавленном «Обработка списков PL / I». Согласно Оксфордский словарь английского языка, то слово указатель впервые появился в печати как указатель стека в техническом меморандуме Корпорация системного развития.

Формальное описание

В Информатика, указатель - это своего рода ссылка.

А примитив данных (или просто примитивный) - это любые данные, которые могут быть прочитаны или записаны в память компьютера используя один доступ к памяти (например, оба байт и слово примитивы).

А совокупность данных (или просто совокупность) - это группа примитивов, которые логически непрерывны в памяти и рассматриваются вместе как один элемент данных (например, агрегат может состоять из 3 логически смежных байтов, значения которых представляют 3 координаты точки в пространстве). Когда агрегат полностью состоит из одного и того же типа примитива, агрегат можно назвать множество; в некотором смысле многобайтовый слово примитив - это массив байтов, и некоторые программы используют слова таким образом.

В контексте этих определений байт самый маленький примитив; каждый адрес памяти указывает другой байт. Адрес памяти начального байта данных считается адресом памяти (или базовый адрес памяти) всей базы данных.

А указатель памяти (или просто указатель) - примитив, значение которого предназначено для использования в качестве адреса памяти; он сказал, что указатель указывает на адрес памяти. Также сказано, что указатель указывает на данные [в памяти] когда значение указателя является адресом памяти данных.

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

Ссылки служат уровнем косвенности: значение указателя определяет, какой адрес памяти (то есть какие данные) должен использоваться в вычислениях. Поскольку косвенность - фундаментальный аспект алгоритмов, указатели часто выражаются как фундаментальные тип данных в языки программирования; в статически (или же сильно ) типизированные языки программирования, тип указателя определяет тип данных, на которые указывает указатель.

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

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

Относительные адреса - это форма руководства сегментация памяти, и разделяют многие его достоинства и недостатки. Двухбайтовое смещение, содержащее 16-битное целое число без знака, может использоваться для обеспечения относительной адресации до 64 KiB (216 байтов) структуры данных. Его можно легко расширить до 128, 256 или 512 Кбайт, если адрес, на который указывает, должен быть выровнен на границе полуслова, слова или двойного слова (но, требуя дополнительного «сдвига влево» побитовая операция - на 1, 2 или 3 бита - для корректировки смещения в 2, 4 или 8 раз перед его добавлением к базовому адресу). Однако, как правило, такие схемы доставляют много проблем, и для удобства программиста абсолютные адреса (и лежащие в основе этого плоское адресное пространство ) является предпочтительным.

Однобайтовое смещение, например шестнадцатеричное. ASCII значение символа (например, X'29 ') может использоваться для указания альтернативного целочисленного значения (или индекса) в массиве (например, X'01'). Таким образом, символы могут быть очень эффективно переведены с 'необработанные данные 'к пригодному к использованию последовательному индекс а затем на абсолютный адрес без Справочная таблица.

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

Таблицы управления которые используются для контроля поток программы обычно широко используют указатели. Указатели, обычно встроенные в запись таблицы, могут, например, использоваться для хранения точек входа в подпрограммы для выполнения на основе определенных условий, определенных в той же записи таблицы. Однако указатели могут быть просто индексами для других отдельных, но связанных таблиц, содержащих массив фактических адресов или самих адресов (в зависимости от доступных конструкций языка программирования). Их также можно использовать для указания на более ранние записи таблицы (как при обработке цикла) или для пересылки, чтобы пропустить некоторые записи таблицы (как в выключатель или «ранний» выход из петли). Для этой последней цели «указатель» может быть просто самим номером записи в таблице и может быть преобразован в фактический адрес простой арифметикой.

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

Указатели очень тонкие абстракция помимо возможностей адресации, предоставляемых самыми современными архитектуры. В простейшей схеме адрес, или числовой индекс, назначается каждой единице памяти в системе, где обычно либо байт или слово - в зависимости от того, есть ли архитектура с байтовой адресацией или же адресный по слову - эффективное преобразование всей памяти в очень большой множество. Затем система также предоставит операцию по извлечению значения, хранящегося в блоке памяти по заданному адресу (обычно с использованием регистры общего назначения ).

В обычном случае указатель достаточно велик, чтобы содержать больше адресов, чем имеется единиц памяти в системе. Это вводит возможность того, что программа может попытаться получить доступ к адресу, который не соответствует ни одной единице памяти, либо потому, что установлено недостаточно памяти (т. Е. Вне диапазона доступной памяти), либо архитектура не поддерживает такие адреса. Первый случай может на некоторых платформах, таких как Intel x86 архитектура, можно назвать ошибка сегментации (ошибка). Второй случай возможен в текущей реализации AMD64, где указатели имеют длину 64 бита, а адреса расширяются только до 48 бит. Указатели должны соответствовать определенным правилам (каноническим адресам), поэтому при разыменовании неканонического указателя процессор вызывает общая ошибка защиты.

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

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

Использует

Указатели напрямую поддерживаются без ограничений на таких языках, как PL / I, C, C ++, Паскаль, FreeBASIC, и неявно в большинстве языки ассемблера. В основном они используются для строительства Рекомендации, которые, в свою очередь, являются основополагающими для построения почти всех структуры данных, а также при передаче данных между различными частями программы.

В языках функционального программирования, которые в значительной степени полагаются на списки, ссылки на данные управляются абстрактно с помощью примитивных конструкций, таких как минусы и соответствующие элементы автомобиль и CDR, которые можно рассматривать как специализированные указатели на первый и второй компоненты cons-ячейки. Это дает начало некоторой идиоматической «разновидности» функционального программирования. Структурируя данные в таких списки минусов эти языки облегчают рекурсивный средство для создания и обработки данных - например, путем рекурсивного доступа к элементам заголовка и хвоста списков списков; например "забирая машину из компакт-диска". Напротив, управление памятью, основанное на разыменовании указателя в некотором приближении множество адресов памяти позволяет рассматривать переменные как слоты, в которые могут быть назначены данные настоятельно.

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

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

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

Указатели C

Базовый синтаксис для определения указателя:[4]

int *ptr;

Это заявляет ptr как идентификатор объекта следующего типа:

  • указатель, указывающий на объект типа int

Обычно это выражается более сжато как "ptr это указатель на int."

Поскольку язык C не определяет неявную инициализацию для объектов с автоматической продолжительностью хранения,[5] Часто необходимо следить за тем, чтобы адрес, по которому ptr баллы действительны; вот почему иногда предлагается явно инициализировать указатель на нулевой указатель значение, которое традиционно указывается в C с помощью стандартизованного макроса НОЛЬ:[6]

int *ptr = НОЛЬ;

Разыменование нулевого указателя в C производит неопределенное поведение,[7] что могло иметь катастрофические последствия. Однако большинство реализаций[нужна цитата ] просто остановите выполнение рассматриваемой программы, обычно с помощью ошибка сегментации.

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

В любом случае, после объявления указателя следующим логическим шагом будет указание на что-то:

int а = 5;int *ptr = НОЛЬ;ptr = &а;

Это присваивает значение адреса а к ptr. Например, если а хранится в ячейке памяти 0x8130, тогда значение ptr будет 0x8130 после присвоения. Чтобы разыменовать указатель, снова используется звездочка:

*ptr = 8;

Это означает взять содержимое ptr (который равен 0x8130), «найдите» этот адрес в памяти и установите для него значение 8. Если а позже снова будет доступен, его новое значение будет 8.

Этот пример может быть более ясным, если непосредственно исследовать память. а находится по адресу 0x8130 в памяти и ptr по адресу 0x8134; также предположим, что это 32-битная машина, так что int имеет 32-битную ширину. Ниже показано, что будет в памяти после выполнения следующего фрагмента кода:

int а = 5;int *ptr = НОЛЬ;
АдресСодержание
0x81300x00000005
0x81340x00000000

(Показанный здесь нулевой указатель равен 0x00000000.) Путем присвоения адреса а к ptr:

 ptr = &а;

дает следующие значения памяти:

АдресСодержание
0x81300x00000005
0x81340x00008130

Затем путем разыменования ptr по кодировке:

 *ptr = 8;

компьютер заберет содержимое ptr (который равен 0x8130), «найдите» этот адрес и присвойте ему 8, в результате чего получится следующая память:

АдресСодержание
0x81300x00000008
0x81340x00008130

Очевидно, что доступ а даст значение 8, потому что предыдущая инструкция изменила содержимое а посредством указателя ptr.

C массивы

В C индексирование массивов формально определяется в терминах арифметики указателей; то есть спецификация языка требует, чтобы массив [я] быть эквивалентным * (массив + я).[8] Таким образом, в C массивы можно рассматривать как указатели на последовательные области памяти (без пропусков),[8] и синтаксис для доступа к массивам идентичен синтаксису, который может использоваться для разыменования указателей. Например, массив множество могут быть объявлены и использованы следующим образом:

int множество[5];      / * Объявляет 5 смежных целых чисел * /int *ptr = множество;  / * Массивы могут использоваться как указатели * /ptr[0] = 1;        / * Указатели можно индексировать с помощью синтаксиса массива * /*(множество + 1) = 2;  / * Массивы можно разыменовать с помощью синтаксиса указателя * /*(1 + множество) = 2;  / * Сложение указателя коммутативно * /множество[2] = 4;      / * Оператор индекса коммутативен * /

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

Хотя большинство операторов для массивов и указателей эквивалентны, результат размер оператор отличается. В этом примере sizeof (массив) будет оценивать 5 * sizeof (число) (размер массива), а sizeof (ptr) будет оценивать sizeof (число *), размер самого указателя.

Значения по умолчанию для массива можно объявить следующим образом:

int множество[5] = {2, 4, 3, 1, 5};

Если множество находится в памяти, начиная с адреса 0x1000 на 32-битной прямой порядок байтов машина, тогда память будет содержать следующее (значения указаны в шестнадцатеричный, вроде адресов):

0123
10002000
10044000
10083000
100C1000
10105000

Здесь представлены пять целых чисел: 2, 4, 3, 1 и 5. Эти пять целых чисел занимают 32 бита (4 байта), каждое из которых младший байт хранится первым (это прямой порядок байтов Архитектура процессора ) и сохраняются последовательно, начиная с адреса 0x1000.

Синтаксис языка C с указателями:

  • множество означает 0x1000;
  • массив + 1 означает 0x1004: "+ 1" означает прибавление размера 1 int, что составляет 4 байта;
  • *множество означает разыменовать содержимое множество. Считая содержимое адресом памяти (0x1000), найдите значение в этом месте (0x0002);
  • массив [я] означает номер элемента я, С 0, из множество что переведено на * (массив + я).

Последний пример - как получить доступ к содержимому множество. Разбивая это:

  • массив + я это место в памяти (i)th элемент множество, начиная с i = 0;
  • * (массив + я) берет этот адрес памяти и разыменовывает его для доступа к значению.

C связанный список

Ниже приведен пример определения связанный список в C.

/ * пустой связанный список представлен NULL * или другое контрольное значение * /#define EMPTY_LIST NULLструктура связь {    пустота        *данные;  / * данные этой ссылки * /    структура связь *следующий;  / * следующая ссылка; EMPTY_LIST, если его нет * /};

Это определение, рекурсивное по указателю, по сути, такое же, как определение рекурсивного по ссылке из Язык программирования Haskell:

 данные Связь а = Ноль             | Минусы а (Связь а)

Ноль это пустой список, а Минусы (ссылка а) это минусы ячейка типа а с другой ссылкой также типа а.

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

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

Указатели могут использоваться для передачи переменных по их адресам, что позволяет изменять их значение. Например, рассмотрим следующие C код:

/ * копию int n можно изменить внутри функции, не затрагивая вызывающий код * /пустота passByValue(int п) {    п = 12;}/ * вместо этого передается указатель m. Копия значения, на которое указывает m, не создается * /пустота passByAddress(int *м) {    *м = 14;}int главный(пустота) {    int Икс = 3;    / * передаем копию значения x в качестве аргумента * /    passByValue(Икс);    // значение было изменено внутри функции, но с этого момента x по-прежнему равен 3    / * передаем адрес x в качестве аргумента * /    passByAddress(&Икс);    // x был фактически изменен функцией и теперь здесь равен 14    возвращаться 0;}

Распределение динамической памяти

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

Указатели используются для хранения и управления адресами динамически распределяется блоки памяти. Такие блоки используются для хранения объектов данных или массивов объектов. Большинство структурированных и объектно-ориентированных языков предоставляют область памяти, называемую куча или же бесплатный магазин, из которых объекты выделяются динамически.

В приведенном ниже примере кода C показано, как объекты структуры динамически выделяются и на них ссылаются. В стандартная библиотека C обеспечивает функцию malloc () для выделения блоков памяти из кучи. Он принимает размер объекта для выделения в качестве параметра и возвращает указатель на вновь выделенный блок памяти, подходящий для хранения объекта, или возвращает нулевой указатель, если выделение не удалось.

/ * Элемент инвентаризации запчастей * /структура Элемент {    int         я бы;     /* Номер части */    char *      имя;   /* Наименование   */    плавать       Стоимость;   /* Расходы        */};/ * Выделить и инициализировать новый объект Item * /структура Элемент * make_item(const char *имя) {    структура Элемент * элемент;    / * Выделяем блок памяти для нового объекта Item * /    элемент = маллок(размер(структура Элемент));    если (элемент == НОЛЬ)        возвращаться НОЛЬ;    / * Инициализируем элементы нового элемента * /    мемсет(элемент, 0, размер(структура Элемент));    элемент->я бы =   -1;    элемент->имя = НОЛЬ;    элемент->Стоимость = 0.0;    / * Сохраняем копию имени в новом элементе * /    элемент->имя = маллок(Strlen(имя) + 1);    если (элемент->имя == НОЛЬ) {        свободный(элемент);        возвращаться НОЛЬ;    }    strcpy(элемент->имя, имя);    / * Возвращаем вновь созданный объект Item * /    возвращаться элемент;}

В приведенном ниже коде показано, как объекты памяти динамически освобождаются, т. Е. Возвращаются в кучу или свободное хранилище. Стандартная библиотека C предоставляет функцию свободный() для освобождения ранее выделенного блока памяти и возврата его обратно в кучу.

/ * Освободить объект Item * /пустота destroy_item(структура Элемент *элемент) {    / * Проверяем указатель на нулевой объект * /    если (элемент == НОЛЬ)        возвращаться;    / * Освободить строку имени, сохраненную в элементе * /    если (элемент->имя != НОЛЬ) {        свободный(элемент->имя);        элемент->имя = НОЛЬ;    }    / * Освободить сам объект Item * /    свободный(элемент);}

Оборудование с отображением памяти

На некоторых вычислительных архитектурах указатели могут использоваться для непосредственного управления памятью или устройствами с отображением в память.

Назначение адресов указателям - бесценный инструмент при программировании. микроконтроллеры. Ниже приведен простой пример объявления указателя типа int и его инициализации как шестнадцатеричный адрес в этом примере константа 0x7FFF:

int *hardware_address = (int *)0x7FFF;

В середине 80-х, используя BIOS доступ к видео возможностям ПК был медленным. Приложения с интенсивным отображением, обычно используемые для доступа CGA видеопамять напрямую путем приведения шестнадцатеричный константа 0xB8000 на указатель на массив из 80 беззнаковых 16-битных значений типа int. Каждое значение состояло из ASCII код в младшем байте и цвет в старшем байте. Таким образом, чтобы поставить букву A в строке 5, столбце 2 ярко-белым цветом на синем, можно было бы написать следующий код:

#define VID ((unsigned short (*) [80]) 0xB8000)пустота фу(пустота) {    VID[4][1] = 0x1F00 | 'А';}

Типизированные указатели и приведение типов

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

Например, в C

int *Деньги;char *сумки;

Деньги будет целочисленным указателем и сумки будет указателем на char. Следующее приведет к предупреждению компилятора о "назначении из несовместимого типа указателя" в разделе GCC

сумки = Деньги;

потому что Деньги и сумки были объявлены с разными типами. Чтобы подавить предупреждение компилятора, необходимо явно указать, что вы действительно хотите выполнить присвоение приведение типов Это

сумки = (char *)Деньги;

который говорит, что нужно привести целочисленный указатель Деньги к указателю char и назначить сумки.

Черновик стандарта C 2005 г. требует, чтобы приведение указателя, производного от одного типа, к другому типу, сохраняло правильность выравнивания для обоих типов (6.3.2.3 Указатели, пар. 7):[9]

char *external_buffer = "abcdef";int *внутренние_данные;внутренние_данные = (int *)external_buffer;  // НЕОПРЕДЕЛЕННОЕ ПОВЕДЕНИЕ if "результирующий указатель                                         // неправильно выровнен "

В языках, которые разрешают арифметику указателей, арифметика указателей учитывает размер типа. Например, добавление целого числа к указателю дает другой указатель, который указывает на адрес, который на это число больше размера типа. Это позволяет нам легко вычислять адреса элементов массива заданного типа, как было показано в приведенном выше примере массивов C. Когда указатель одного типа приводится к другому типу другого размера, программист должен ожидать, что арифметика указателя будет вычисляться по-другому. В C, например, если Деньги массив начинается с 0x2000 и sizeof (число) составляет 4 байта, тогда как sizeof (символ) 1 байт, тогда деньги + 1 будет указывать на 0x2004, но сумки + '1' будет указывать на 0x2001. Другие риски трансляции включают потерю данных, когда «широкие» данные записываются в «узкие» места (например, мешки [0] = 65537;), неожиданные результаты при битовый сдвиг значения и проблемы сравнения, особенно со знаковыми и беззнаковыми значениями.

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

Повышение безопасности указателей

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

Одна из основных проблем с указателями заключается в том, что до тех пор, пока ими можно напрямую манипулировать как числа, они могут указывать на неиспользуемые адреса или на данные, которые используются для других целей. Многие языки, в том числе большинство функциональные языки программирования и недавний императивные языки подобно Ява, замените указатели на более непрозрачный тип ссылки, обычно называемый просто ссылка, который может использоваться только для ссылки на объекты и не может использоваться как числа, что предотвращает этот тип ошибок. Индексирование массива рассматривается как особый случай.

Указатель, которому не назначен адрес, называется дикий указатель. Любая попытка использовать такие неинициализированные указатели может вызвать непредвиденное поведение либо потому, что начальное значение не является допустимым адресом, либо потому, что его использование может повредить другие части программы. Результатом часто бывает ошибка сегментации, нарушение хранения или же дикая ветка (если используется как указатель функции или адрес ветвления).

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

Некоторые языки, например C ++, поддерживать умные указатели, которые используют простую форму подсчет ссылок чтобы помочь отслеживать распределение динамической памяти в дополнение к работе в качестве ссылки. В отсутствие циклов ссылок, когда объект косвенно ссылается на себя через последовательность интеллектуальных указателей, они исключают возможность «висящих» указателей и утечек памяти. Delphi строки изначально поддерживают подсчет ссылок.

В Язык программирования Rust вводит чекер, время жизни указателя, и оптимизация, основанная на необязательные типы за нулевые указатели устранить ошибки указателя, не прибегая к вывоз мусора.

Нулевой указатель

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

Автоотносительный указатель

An автоотносительный указатель - указатель, значение которого интерпретируется как смещение от адреса самого указателя; таким образом, если структура данных имеет элемент автоотносительного указателя, который указывает на некоторую часть самой структуры данных, то структура данных может быть перемещена в память без необходимости обновления значения автоматического относительного указателя.[10]

В цитируемом патенте также используется термин относительный указатель означать то же самое. Однако значение этого термина использовалось и по-другому:

  • означать смещение от адреса структуры, а не от адреса самого указателя;[нужна цитата ]
  • для обозначения указателя, содержащего собственный адрес, который может быть полезен для восстановления в любой произвольной области памяти набора структур данных, которые указывают друг на друга.[11]

Указатель на основе

А указатель на основе - указатель, значение которого является смещением от значения другого указателя. Это может быть использовано для хранения и загрузки блоков данных, присвоения адреса начала блока базовому указателю.[12]

Множественное косвенное обращение

В некоторых языках указатель может ссылаться на другой указатель, что требует нескольких операций разыменования для перехода к исходному значению. Хотя каждый уровень косвенного обращения может увеличить производительность, иногда это необходимо для обеспечения правильного поведения для сложных структуры данных. Например, в C обычно определяют связанный список в терминах элемента, который содержит указатель на следующий элемент списка:

структура элемент {    структура элемент *следующий;    int            ценить;};структура элемент *голова = НОЛЬ;

Эта реализация использует указатель на первый элемент в списке как суррогат для всего списка. Если новое значение добавляется в начало списка, голова должен быть изменен, чтобы указывать на новый элемент. Поскольку аргументы C всегда передаются по значению, использование двойного косвенного обращения позволяет правильно реализовать вставку и имеет желаемый побочный эффект в виде исключения кода особого случая для обработки вставок в начале списка:

// Учитывая отсортированный список в * head, вставляем элемент элемента в первый// место, где все предыдущие элементы имеют меньшее или равное значение.пустота вставлять(структура элемент **голова, структура элемент *элемент) {    структура элемент **п;  // p указывает на указатель на элемент    за (п = голова; *п != НОЛЬ; п = &(*п)->следующий) {        если (элемент->ценить <= (*п)->ценить)            перемена;    }    элемент->следующий = *п;    *п = элемент;}// Вызывающий делает это:вставлять(&голова, элемент);

В этом случае, если значение элемент меньше, чем у голова, звонящий голова правильно обновляется по адресу нового элемента.

Базовый пример находится в argv аргумент к основная функция в C (и C ++), который в прототипе задан как char ** argv- это потому, что переменная argv сам по себе является указателем на массив строк (массив массивов), поэтому * argv - указатель на 0-ю строку (по соглашению название программы), а ** argv это 0-й символ 0-й строки.

Указатель функции

В некоторых языках указатель может ссылаться на исполняемый код, т. Е. Указывать на функцию, метод или процедуру. А указатель на функцию будет хранить адрес вызываемой функции. Хотя это средство можно использовать для динамического вызова функций, оно часто является излюбленным приемом авторов вирусов и других вредоносных программ.

int сумма(int n1, int n2) {   // Функция с двумя целочисленными параметрами, возвращающая целочисленное значение    возвращаться n1 + n2;}int главный(пустота) {    int а, б, Икс, у;    int (*fp)(int, int);    // Указатель на функцию, который может указывать на такую ​​функцию, как сумма    fp = &сумма;              // fp теперь указывает на сумму функции    Икс = (*fp)(а, б);        // Вызов функции sum с аргументами a и b    у = сумма(а, б);          // Вызов функции sum с аргументами a и b}

Висячий указатель

А висячий указатель - это указатель, который не указывает на действительный объект и, следовательно, может привести к сбою программы или странному поведению. в Паскаль или же Языки программирования C, указатели, которые специально не инициализированы, могут указывать на непредсказуемые адреса в памяти.

В следующем примере кода показан висящий указатель:

int func(пустота) {    char *p1 = маллок(размер(char)); / * (undefined) значение некоторого места в куче * /    char *p2;       / * висячий (неинициализированный) указатель * /    *p1 = 'а';      / * Это нормально, если malloc () не вернул NULL. * /    *p2 = 'b';      / * Это вызывает неопределенное поведение * /}

Здесь, p2 может указывать на любое место в памяти, поэтому выполнение присваивания * p2 = 'b'; может повредить неизвестную область памяти или вызвать ошибка сегментации.

Обратный указатель

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

Дикая ветка

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

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

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

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

Это даже теоретически возможно, используя описанную выше технику вместе с подходящим симулятор набора команд моделировать любой Машинный код или промежуточный (байтовый код ) из любой процессор / язык на другом языке, который вообще не поддерживает указатели (например, Ява / JavaScript ). Для этого двоичный Первоначально код может быть загружен в смежные байты массива для симулятора для «чтения», интерпретации и действий полностью в памяти, содержащейся в том же массиве. При необходимости, чтобы полностью избежать переполнение буфера проблемы, проверка границ обычно может выполняться для компилятора (или, если нет, вручную в симуляторе).

Поддержка различных языков программирования

Ада

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

БАЗОВЫЙ

Несколько старых версий БАЗОВЫЙ для платформы Windows была поддержка STRPTR () для возврата адреса строки и для VARPTR () для возврата адреса переменной. Visual Basic 5 также поддерживает функцию OBJPTR () для возврата адреса объектного интерфейса и для оператора ADDRESSOF для возврата адреса функции. Все они являются целыми числами, но их значения эквивалентны значениям указателей.

Новые диалекты БАЗОВЫЙ, Такие как FreeBASIC или же BlitzMax, однако, имеют исчерпывающие реализации указателей. В FreeBASIC арифметика на ЛЮБОЙ указатели (эквивалентные C пустота*) рассматриваются как если бы ЛЮБОЙ указатель был шириной в байтах. ЛЮБОЙ указатели не могут быть разыменованы, как в C. Кроме того, приведение между ЛЮБОЙ и указатели любого другого типа не будут генерировать никаких предупреждений.

тусклый в качестве целое число ж = 257тусклый в качестве любой ptr грамм = @жтусклый в качестве целое число ptr я = граммутверждать(*я = 257)утверждать( (грамм + 4) = (@ж + 1) )

C и C ++

В C и C ++ указатели - это переменные, которые хранят адреса и могут быть ноль. Каждый указатель имеет тип, на который он указывает, но можно произвольно преобразовывать типы указателей (но не между указателем функции и указателем объекта). Специальный тип указателя, называемый «пустым указателем», позволяет указывать на любой (нефункциональный) объект, но ограничен тем, что его нельзя разыменовать напрямую (он должен быть приведен). Самым адресом часто можно напрямую манипулировать, приводя указатель к целочисленному типу достаточного размера и от него, хотя результаты определяются реализацией и действительно могут вызывать неопределенное поведение; в то время как более ранние стандарты C не имели интегрального типа, который гарантированно был достаточно большим, C99 определяет uintptr_t typedef имя определено в <stdint.h>, но реализация не обязана это предоставлять.

C ++ полностью поддерживает указатели C и приведение типов C. Он также поддерживает новую группу операторов приведения типов, чтобы помочь отловить некоторые непреднамеренные опасные преобразования во время компиляции. С C ++ 11, то Стандартная библиотека C ++ также предоставляет умные указатели (unique_ptr, shared_ptr и weak_ptr), который может использоваться в некоторых ситуациях как более безопасная альтернатива примитивным указателям C. C ++ также поддерживает другую форму ссылки, совершенно отличную от указателя, которая называется просто ссылка или же ссылочный тип.

Указатель арифметики, то есть возможность изменять целевой адрес указателя с помощью арифметических операций (а также сравнения величин) ограничивается стандартом языка, чтобы оставаться в пределах одного объекта массива (или сразу после него), и в противном случае вызовет неопределенное поведение. Добавление или вычитание указателя перемещает его на величину, кратную его размеру. тип данных. Например, добавление 1 к указателю на 4-байтовые целочисленные значения увеличит адрес байта указателя указателя на 4. Это приведет к увеличению указателя, чтобы он указывал на следующий элемент в непрерывном массиве целых чисел, то есть часто желаемый результат. Арифметика указателя не может выполняться на пустота указатели, потому что тип пустоты не имеет размера, поэтому указанный адрес не может быть добавлен, хотя gcc и другие компиляторы будут выполнять байтовую арифметику на пустота* как нестандартное расширение, рассматривая его как символ *.

Арифметика указателей предоставляет программисту единый способ работы с различными типами: добавление и вычитание количества требуемых элементов вместо фактического смещения в байтах. (Указатель арифметики с символ * указатели используют байтовые смещения, потому что sizeof (символ) равен 1 по определению.) В частности, определение C явно заявляет, что синтаксис а [п], какой п-й элемент массива а, эквивалентно * (а + п), который является содержанием элемента, на который указывает а + п. Отсюда следует, что п [а] эквивалентно а [п], и можно написать, например, а [3] или же 3 [а] одинаково хорошо для доступа к четвертому элементу массива а.

В то время как арифметика с указателями является мощной, она может быть источником компьютерные ошибки. Это сбивает с толку новичка программисты, заставляя их попадать в разные контексты: выражение может быть обычным арифметическим или арифметическим с указателем, и иногда одно легко спутать с другим. В ответ на это многие современные компьютерные языки высокого уровня (например, Ява ) не разрешают прямой доступ к памяти с использованием адресов. Кроме того, безопасный диалект C Циклон решает многие проблемы с указателями. Видеть Язык программирования C для дальнейшего обсуждения.

В пустота указатель, или же пустота*, поддерживается в ANSI C и C ++ как универсальный тип указателя. Указатель на пустота может хранить адрес любого объекта (не функции) и, в C, неявно преобразуется в любой другой тип указателя объекта при назначении, но он должен быть явно приведен при разыменовании.K&R C используется символ * для цели «указатель, не зависящий от типа» (до ANSI C).

int Икс = 4;пустота* p1 = &Икс;int* p2 = p1;       // void * неявно преобразовано в int *: допустимый C, но не C ++int а = *p2;int б = *(int*)p1;  // при разыменовании inline неявного преобразования нет

C ++ не позволяет неявное преобразование пустота* на другие типы указателей, даже в присваиваниях. Это было дизайнерское решение, чтобы избежать неосторожных и даже непреднамеренных приведений, хотя большинство компиляторов выводят только предупреждения, а не ошибки при обнаружении других приведений.

int Икс = 4;пустота* p1 = &Икс;int* p2 = p1;                     // это не удается в C ++: нет неявного преобразования из void *int* p3 = (int*)p1;               // Приведение в стиле Cint* p4 = static_cast<int*>(p1);  // C ++ приведение

В C ++ нет пустота& (ссылка на недействительна) в дополнение пустота* (указатель на void), потому что ссылки ведут себя как псевдонимы переменных, на которые они указывают, и никогда не может быть переменной, тип которой пустота.

Обзор синтаксиса объявления указателя

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

char cff [5][5];    / * массив массивов символов * /char *cfp [5];      / * массив указателей на символы * /char **cpp;         / * указатель на указатель на char ("двойной указатель") * /char (*cpf) [5];    / * указатель на массив (ы) символов * /char *cpF();        / * функция, возвращающая указатель на char (s) * /char (*CFp)();      / * указатель на функцию, которая возвращает символ * /char (*cfpF())[5];  / * функция, возвращающая указатель на массив символов * /char (*cpFf[5])();  / * массив указателей на функции, возвращающие символ * /

() И [] имеют более высокий приоритет, чем *.[13]

C #

в Язык программирования C #, указатели поддерживаются только при определенных условиях: любой блок кода, включая указатели, должен быть помечен знаком небезопасно ключевое слово. Такие блоки обычно требуют более высоких разрешений безопасности для запуска. Синтаксис по существу такой же, как в C ++, а указанный адрес может быть либо удалось или же неуправляемый объем памяти. Однако указатели на управляемую память (любой указатель на управляемый объект) должны быть объявлены с использованием фиксированный ключевое слово, которое предотвращает уборщик мусора от перемещения заостренного объекта как части управления памятью, пока указатель находится в области видимости, тем самым сохраняя адрес указателя действительным.

Исключением является использование IntPtr структура, которая является безопасным управляемым эквивалентом int *, и не требует небезопасного кода. Этот тип часто возвращается при использовании методов из System.Runtime.InteropServices, Например:

// Получаем 16 байт памяти из неуправляемой памяти процессаIntPtr указатель = Система.Время выполнения.InteropServices.Маршал.AllocHGlobal(16);// Что-то делаем с выделенной памятью// Освобождаем выделенную памятьСистема.Время выполнения.InteropServices.Маршал.FreeHGlobal(указатель);

В .NET Framework включает в себя множество классов и методов в Система и System.Runtime.InteropServices пространства имен (например, Маршал class), которые конвертируют типы .NET (например, System.String) ко многим и от многих неуправляемый типы и указатели (например, LPWSTR или же пустота*), чтобы разрешить общение с неуправляемый код. Большинство таких методов имеют те же требования к разрешениям безопасности, что и неуправляемый код, поскольку они могут влиять на произвольные места в памяти.

КОБОЛ

В КОБОЛ язык программирования поддерживает указатели на переменные. Примитивные или групповые (записи) объекты данных, объявленные в РАЗДЕЛ СВЯЗИ программы по своей сути основаны на указателях, где единственная память, выделенная в программе, - это пространство для адреса элемента данных (обычно одно слово памяти). В исходном коде программы эти элементы данных используются так же, как и любые другие. РАБОТА-ХРАНЕНИЕ переменная, но доступ к их содержимому косвенно осуществляется через их СВЯЗЬ указатели.

Пространство памяти для каждого указанного объекта данных обычно распределяется динамически используя внешние ВЫЗОВ операторы или с помощью встроенных расширенных языковых конструкций, таких как EXEC CICS или же EXEC SQL заявления.

Расширенные версии COBOL также предоставляют переменные-указатели, объявленные с помощью ИСПОЛЬЗОВАНИЕ ЯВЛЯЕТСЯ УКАЗАТЕЛЬ статьи. Значения таких переменных-указателей устанавливаются и изменяются с помощью НАБОР и НАБОР АДРЕС заявления.

Некоторые расширенные версии COBOL также предоставляют УКАЗАТЕЛЬ ПРОЦЕДУРЫ переменные, которые могут хранить адреса исполняемого кода.

PL / I

В PL / I язык обеспечивает полную поддержку указателей на все типы данных (включая указатели на структуры), рекурсия, многозадачность, обработка строк и обширные встроенные функции. PL / I был большим шагом вперед по сравнению с языками программирования того времени.[нужна цитата ] Указатели PL / I не типизированы, и поэтому для разыменования или присваивания указателей не требуется приведение типов. Синтаксис объявления для указателя: ОБЪЯВИТЬ ХХХ УКАЗАТЕЛЬ;, который объявляет указатель с именем «xxx». Указатели используются с ОСНОВАН переменные. Базовая переменная может быть объявлена ​​с помощью локатора по умолчанию (ЗАЯВИТЬ НА ОСНОВЕ xxx (ppp); или без (ЗАЯВИТЬ НА ОСНОВЕ xxx;), где xxx - базовая переменная, которая может быть переменной элемента, структурой или массивом, а ppp - указателем по умолчанию). Такая переменная может быть адресной без явной ссылки на указатель (ххх = 1;, или может быть адресован с явной ссылкой на локатор по умолчанию (ppp) или на любой другой указатель (qqq-> ххх = 1;).

Арифметика указателей не является частью стандарта PL / I, но многие компиляторы допускают выражения вида ptr = ptr ± выражение. IBM PL / I также имеет встроенную функцию PTRADD выполнить арифметику. Арифметика указателя всегда выполняется в байтах.

IBM Предприятие В компиляторах PL / I появилась новая форма типизированного указателя, называемая РУЧКА.

D

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

Эйфель

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

Фортран

Фортран-90 представила возможность строго типизированного указателя. Указатели Fortran содержат больше, чем просто адрес памяти. Они также инкапсулируют нижнюю и верхнюю границы размеров массива, шаги (например, для поддержки произвольных разделов массива) и другие метаданные. An оператор ассоциации, => используется для связывания УКАЗАТЕЛЬ к переменной, имеющей ЦЕЛЬ атрибут. Фортран-90 РАСПРЕДЕЛЕНИЕ Оператор также может использоваться для связывания указателя с блоком памяти. Например, следующий код можно использовать для определения и создания структуры связанного списка:

тип real_list_t  настоящий :: Пример данных(100)  тип (real_list_t), указатель :: следующий => ноль ()тип концатип (real_list_t), цель :: my_real_listтип (real_list_t), указатель :: real_list_tempreal_list_temp => my_real_listделать  читать (1,iostat=ioerr) real_list_temp%Пример данных  если (ioerr /= 0) выход  выделить (real_list_temp%следующий)  real_list_temp => real_list_temp%следующийконец делать

Fortran-2003 добавляет поддержку указателей процедур. Также в рамках C Взаимодействие Функция Fortran-2003 поддерживает встроенные функции для преобразования указателей в стиле C в указатели Fortran и обратно.

Идти

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

Ява

В отличие от C, C ++, или же Паскаль, нет явного представления указателей в Ява. Вместо этого более сложные структуры данных, такие как объекты и массивы реализуются с использованием Рекомендации. Язык не предоставляет никаких явных операторов манипулирования указателями. Однако код все еще может попытаться разыменовать нулевую ссылку (нулевой указатель), что приведет к исключение быть брошенным. Пространство, занятое объектами памяти, на которые нет ссылок, автоматически восстанавливается вывоз мусора во время выполнения.[14]

Модула-2

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

Оберон

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

Паскаль

В отличие от многих языков, в которых есть указатели, стандартные ISO Паскаль только позволяет указателям ссылаться на динамически созданные переменные, которые являются анонимными, и не позволяет им ссылаться на стандартные статические или локальные переменные.[15] У него нет арифметики указателей. Указатели также должны иметь связанный тип, и указатель на один тип несовместим с указателем на другой тип (например, указатель на char несовместим с указателем на целое число). Это помогает устранить проблемы безопасности типов, присущие другим реализациям указателей, особенно тем, которые используются для PL / I или же C. Это также устраняет некоторые риски, вызванные висячие указатели, но возможность динамически освободить ссылочное пространство с помощью избавляться стандартная процедура (которая имеет тот же эффект, что и свободный библиотечная функция найдена в C ) означает, что риск зависания указателей полностью исключен.[16]

Однако в некоторых коммерческих реализациях компилятора Pascal (или производных) с открытым исходным кодом, например Free Pascal,[17] Турбо Паскаль или Object Pascal в Embarcadero Delphi - указатель может ссылаться на стандартные статические или локальные переменные и может быть приведен от одного типа указателя к другому. Более того, арифметика указателя не ограничена: добавление или вычитание из указателя перемещает его на указанное количество байтов в любом направлении, но с использованием Inc или же Декабрь стандартные процедуры с его помощью перемещают указатель на размер тип данных это объявлен указать на. Нетипизированный указатель также предоставляется под именем Указатель, который совместим с другими типами указателей.

Perl

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

Смотрите также

Рекомендации

  1. ^ Дональд Кнут (1974). «Структурированное программирование с использованием операторов перехода» (PDF). Вычислительные опросы. 6 (5): 261–301. CiteSeerX  10.1.1.103.6084. Дои:10.1145/356635.356640. S2CID  207630080. Архивировано из оригинал (PDF) 24 августа 2009 г.
  2. ^ Рейли, Эдвин Д. (2003). Вехи компьютерных наук и информационных технологий. Издательская группа «Гринвуд». п.204. ISBN  9781573565219. Получено 2018-04-13. Указатель Гарольда Лоусона.
  3. ^ "Список наград IEEE Computer Society". Awards.computer.org. Архивировано из оригинал на 2011-03-22. Получено 2018-04-13.
  4. ^ ISO / IEC 9899, п. 6.7.5.1 п. 1.
  5. ^ ISO / IEC 9899, п. 6.7.8 п. 10.
  6. ^ ISO / IEC 9899, п. 7.17, п. 3: NULL ... который расширяется до константы нулевого указателя, определенной реализацией ...
  7. ^ ISO / IEC 9899, пункт 6.5.3.2, абзац 4, сноска 87: Если указателю было присвоено недопустимое значение, поведение унарного оператора * не определено ... Среди недопустимых значений для разыменования указателя унарным оператором * есть нулевой указатель ...
  8. ^ а б Plauger, P J; Броди, Джим (1992). Справочник программиста по стандарту C ANSI и ISO. Редмонд, Вашингтон: Microsoft Press. стр.108, 51. ISBN  978-1-55615-359-4. Тип массива не содержит дополнительных отверстий, потому что все другие типы плотно упаковываются при объединении в массивы. [на странице 51]
  9. ^ WG14 N1124, C - Утвержденные стандарты: ISO / IEC 9899 - Языки программирования - C, 2005-05-06.
  10. ^ патент США 6625718, Штайнер, Роберт С. (Брумфилд, Колорадо), «Указатели, относящиеся к их собственному нынешнему местоположению», выпущенный 23 сентября 2003 г., передан Avaya Technology Corp. (Баскинг-Ридж, Нью-Джерси) 
  11. ^ патент США 6115721, Надь, Майкл (Тампа, Флорида), «Система и метод для сохранения и восстановления базы данных с использованием самонаведения», выпущенный 05 сентября 2000 г., передан IBM (Армонк, Нью-Йорк) 
  12. ^ «Основанные указатели». Msdn.microsoft.com. Получено 2018-04-13.
  13. ^ Ульф Билтинг, Ян Скансхольм, "Vägen till C" (Дорога к C), третье издание, стр. 169, ISBN  91-44-01468-6
  14. ^ Ник Парланте, [1], Стэнфордская образовательная библиотека компьютерных наук 2000. С. 9–10.
  15. ^ Стандарт ISO 7185 Pascal (неофициальная копия), раздел 6.4.4 Типы указателей и последующие.
  16. ^ Дж. Уэлш, В. Дж. Снирингер и К. А. Р. Хоар, «Двусмысленность и ненадежность в Паскале», Практика и опыт работы с программным обеспечением 7, стр. 685–696 (1977).
  17. ^ Справочное руководство Free Pascal Language, раздел 3.4 Указатели
  18. ^ Контактная информация. "// Создание ссылок (ссылки на Perl и вложенные структуры данных)". Perldoc.perl.org. Получено 2018-04-13.

внешняя ссылка