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

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

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

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

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

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

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

История [ править ]

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

Официальное описание [ править ]

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

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

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

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

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

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

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

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

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

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

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

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

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

Архитектурные корни [ править ]

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

В обычном случае указатель достаточно велик, чтобы содержать больше адресов, чем имеется единиц памяти в системе. Это вводит возможность того, что программа может попытаться получить доступ к адресу, который не соответствует ни одной единице памяти, либо потому, что установлено недостаточно памяти (т. Е. За пределами диапазона доступной памяти), либо архитектура не поддерживает такие адреса. Первый случай на некоторых платформах, таких как архитектура 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-списках , эти языки упрощают рекурсивные средства для создания и обработки данных - например, путем рекурсивного доступа к элементам заголовка и хвоста списков списков; например, «забрать машину с компакт-диска». Напротив, управление памятью на основе разыменования указателя в некотором приближении массиваадресов памяти позволяет рассматривать переменные как слоты, в которые могут быть назначены данные императивно .

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

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

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

Указатели C [ править ]

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

int  * ptr ;

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

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

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

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

int  * ptr  =  NULL ;

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

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

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

int  a  =  5 ; int  * ptr  =  NULL ;ptr  =  & a ;

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

* ptr  =  8 ;

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

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

int  a  =  5 ; int  * ptr  =  NULL ;

(Указатель NULL , показанный здесь 0x00000000.) Назначив адрес aдля ptr:

 ptr  =  & a ;

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

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

 * ptr  =  8 ;

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

Ясно, что при доступе aбудет получено значение 8, потому что предыдущая инструкция изменила содержимое с aпомощью указателя ptr.

Массивы C [ править ]

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

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

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

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

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

int  array [ 5 ]  =  { 2 ,  4 ,  3 ,  1 ,  5 };

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

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

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

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

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

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

C связанный список [ править ]

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

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

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

 data  Link  a  =  Nil  |  Против  ( Link а )  

Nil- пустой список, Cons a (Link a)являющийся cons- ячейкой типа aс другой ссылкой, также имеющей тип a.

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

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

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

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

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

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

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

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

/ * Элемент инвентаризации запчастей * / struct  Item  {  int  id ;  / * Номер детали * /  char  *  name ;  / * Название детали * /  float  cost ;  / * Стоимость * / };/ * Выделить и инициализировать новый объект Item * / struct  Item  *  make_item ( const  char  * name )  {  struct  Item  *  item ; / * Выделяем блок памяти для нового объекта Item * /  item  =  malloc ( sizeof ( struct  Item ));  если  ( item  ==  NULL )  вернуть  NULL ; / * Инициализируем элементы нового элемента * /  memset ( item ,  0 ,  sizeof ( struct  Item ));  элемент -> id  =  -1 ;  элемент -> имя  =  NULL ;  элемент -> стоимость  =  0,0 ; / * Сохраняем копию имени в новом  элементе * / item -> name  =  malloc ( strlen ( name )  +  1 );  если  ( элемент -> имя  ==  NULL )  {  бесплатно ( элемент );  return  NULL ;  }  strcpy ( элемент -> имя ,  имя ); / * Возвращаем вновь созданный объект Item * /  return  item ; }

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

/ * Освобождение объекта Item * / void  destroy_item ( struct  Item  * item )  {  / * Проверка указателя на нулевой объект * /  if  ( item  ==  NULL )  return ; / * Освободить строку имени, сохраненную в  элементе * / if  ( item -> name  ! =  NULL )  {  free ( item -> name );  элемент -> имя  =  NULL ;  } / * Освободить сам объект Item * /  free ( 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)void  foo ( void )  {  VID [ 4 ] [ 1 ]  =  0x1F00  |  'А' ; }

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

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

Например, в C

int  * деньги ; чар  * мешки ;

moneyбудет целочисленным указателем и указателем bagsна символ. Следующее приведет к предупреждению компилятора о «назначении из несовместимого типа указателя» в GCC.

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

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

мешки  =  ( char  * ) деньги ;

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

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

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

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

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

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

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

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

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

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

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

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

Нулевой указатель [ править ]

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

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

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

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

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

Указатель на основе [ править ]

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

Множественное косвенное обращение [ править ]

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

структура  элемента  {  структура  элемента  * следующий ;  значение int  ; }; элемент  структуры * head  =  NULL ;

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

// Учитывая отсортированный список в * head, вставьте элемент элемента в первое // место, где все предыдущие элементы имеют меньшее или равное значение. void  insert ( struct  element  ** head ,  struct  element  * item )  {  struct  element  ** p ;  // p указывает на указатель на элемент  for  ( p  =  head ;  * p  ! =  NULL ;  p  =  & ( * p ) -> next )  { if  ( item -> value  <=  ( * p ) -> value )  break ;  }  элемент -> следующий  =  * p ;  * p  =  элемент ; }// Вызывающий делает это: insert ( & head ,  item );

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

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

Указатель функции [ править ]

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

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

Висячий указатель [ править ]

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

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

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

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

Обратный указатель [ править ]

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

Дикая ветка [ править ]

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

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

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

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

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

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

Ада [ править ]

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

BASIC [ править ]

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

Однако более новые диалекты BASIC , такие как FreeBASIC или BlitzMax , имеют исчерпывающие реализации указателей. В FreeBASIC арифметические операции с ANYуказателями (эквивалентные C void*) обрабатываются так, как если бы ANYуказатель был байтовой шириной. ANYуказатели не могут быть разыменованы, как в C. Кроме того, преобразование между ANYуказателями и указателями любого другого типа не будет генерировать никаких предупреждений.

dim  как  целое число  f  =  257 dim  как  любое  ptr  g  =  @ f dim  как  целое  ptr  i  =  g assert ( * i  =  257 ) assert (  ( g  +  4 )  =  ( @ f  +  1 )  )

C и C ++ [ править ]

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

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

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

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

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

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

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

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

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

В C ++ нет void&(ссылки на void) для дополнения void*(указатель на void), потому что ссылки ведут себя как псевдонимы для переменных, на которые они указывают, и никогда не может быть переменной с типом 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 # указатели поддерживаются только при определенных условиях: любой блок кода, включая указатели, должен быть помечен unsafeключевым словом. Такие блоки обычно требуют более высоких разрешений безопасности для запуска. Синтаксис по существу такой же, как в C ++, а указанный адрес может быть управляемой или неуправляемой памятью. Однако указатели на управляемую память (любой указатель на управляемый объект) должны быть объявлены с использованием fixedключевого слова, которое не позволяет сборщику мусора перемещать указанный объект в рамках управления памятью, пока указатель находится в области видимости, тем самым сохраняя адрес указателя действительным.

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

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

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

КОБОЛ [ править ]

Язык программирования COBOL поддерживает указатели на переменные. Объекты данных примитивов или групп (записей), объявленные в пределах LINKAGE SECTIONпрограммы, по своей сути основаны на указателях, где единственная память, выделенная в программе, - это пространство для адреса элемента данных (обычно одного слова памяти). В исходном коде программы эти элементы данных используются так же, как и любые другие WORKING-STORAGEпеременные, но к их содержимому неявно осуществляется косвенный доступ через их LINKAGEуказатели.

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

Расширенные версии COBOL также предоставляют переменные-указатели, объявленные с USAGE IS POINTERпредложениями. Значения таких переменных указателей устанавливаются и изменяются с помощью SETи SET ADDRESSзаявления.

Некоторые расширенные версии COBOL также предоставляют PROCEDURE-POINTERпеременные, которые могут хранить адреса исполняемого кода .

PL / I [ править ]

Язык PL / I обеспечивает полную поддержку указателей на все типы данных (включая указатели на структуры), рекурсию , многозадачность , обработку строк и обширные встроенные функции . PL / I был большим шагом вперед по сравнению с языками программирования того времени. [ необходима цитата ] Указатели PL / I не типизированы, и поэтому для разыменования или присвоения указателя не требуется приведение типов. Синтаксис объявления указателя: DECLARE xxx POINTER;объявляет указатель с именем «xxx». Указатели используются с BASEDпеременными. Базовая переменная может быть объявлена ​​с локатором по умолчанию ( DECLARE xxx BASED(ppp);или без (DECLARE xxx BASED;), где xxx - базовая переменная, которая может быть переменной элемента, структурой или массивом, а ppp - указателем по умолчанию). Такая переменная может иметь адрес без явной ссылки на указатель ( xxx=1;или может быть адресована с явной ссылкой на локатор по умолчанию (ppp) или на любой другой указатель ( qqq->xxx=1;).

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

Компиляторы IBM Enterprise PL / I имеют новую форму типизированного указателя, называемую HANDLE.

D [ править ]

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

Эйфель [ править ]

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

Фортран [ править ]

Fortran-90 представил возможность строго типизированного указателя. Указатели Fortran содержат больше, чем просто адрес памяти. Они также инкапсулируют нижнюю и верхнюю границы размеров массива, шаги (например, для поддержки произвольных разделов массива) и другие метаданные. Оператор объединения , =>используется для связывания POINTERпеременной , которая имеет TARGETатрибут. Оператор Fortran-90 ALLOCATEтакже может использоваться для связывания указателя с блоком памяти. Например, следующий код можно использовать для определения и создания структуры связанного списка:

тип real_list_t  real  ::  sample_data ( 100 )  тип  ( real_list_t ),  указатель  ::  next  =>  null  () тип концатип  ( real_list_t ),  target  ::  my_real_list тип  ( real_list_t ),  указатель  ::  real_list_tempreal_list_temp  =>  my_real_list сделать  чтения  ( 1 , IOSTAT = ioerr )  real_list_temp % sample_data ,  если  ( ioerr  / =  0 )  выход  выделить  ( real_list_temp % в следующем )  real_list_temp  =>  real_list_temp % в следующем конце концов делать

Fortran-2003 добавляет поддержку указателей процедур. Кроме того, как часть функциональной совместимости C , Fortran-2003 поддерживает встроенные функции для преобразования указателей в стиле C в указатели Fortran и обратно.

Перейти [ править ]

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

Java [ править ]

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

Модула-2 [ править ]

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

Оберон [ править ]

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

Паскаль [ править ]

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

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

Perl [ править ]

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

См. Также [ править ]

  • Адресная константа
  • Ограниченный указатель
  • Переполнение буфера
  • Толстый указатель
  • Указатель функции
  • Указатель опасности
  • Непрозрачный указатель
  • Указатель swizzling
  • Справочник (информатика)
  • Статический анализ программы
  • Нарушение хранилища
  • Указатель с тегами
  • Переменная (информатика)
  • Нумерация с нуля
  • Итератор

Ссылки [ править ]

  1. ^ Дональд Кнут (1974). «Структурированное программирование с использованием операторов перехода» (PDF) . Вычислительные обзоры . 6 (5): 261–301. CiteSeerX  10.1.1.103.6084 . DOI : 10.1145 / 356635.356640 . S2CID  207630080 . Архивировано из оригинального (PDF) 24 августа 2009 года.
  2. ^ Рейли, Эдвин Д. (2003). Основные этапы развития компьютерных наук и информационных технологий . Издательская группа «Гринвуд». п. 204 . ISBN 9781573565219. Проверено 13 апреля 2018 . Указатель Гарольда Лоусона.
  3. ^ "Список наград IEEE Computer Society" . Awards.computer.org. Архивировано из оригинала на 2011-03-22 . Проверено 13 апреля 2018 .
  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. ^ a b Plauger, PJ ; Броди, Джим (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 , Steiner, Роберт С. (Брумфилд, CO), «Указателей, которые по отношению к своим собственным нынешним местам», выданных 2003-09-23, присвоенных Avaya Technology Corp. (Баскин - Ридж, Нью - Джерси) 
  11. ^ Патент США 6115721 , Nagy, Майкл (Tampa, FL), «Система и метод для базы данных Сохранения и восстановление с помощью самих-указателей», выданного 2000-09-05, назначено IBM (Армонк, штат Нью - Йорк) 
  12. ^ "Основанные указатели" . Msdn.microsoft.com . Проверено 13 апреля 2018 .
  13. ^ Ульф Билтинг, Ян Скансхольм, "Vägen till C" (Дорога к C), третье издание, стр. 169, ISBN 91-44-01468-6 
  14. ^ Ник Парланте, [1] , Стэнфордская образовательная библиотека по информатике , стр. 9–10 (2000).
  15. ^ Стандарт ISO 7185 Pascal (неофициальная копия), раздел 6.4.4 Типы указателей и последующие.
  16. ^ J. Welsh, WJ Sneeringer и Хоар, "неясности и Ненадежность в Паскаль," Программное обеспечение Практика и опыт 7 , стр. 685-696 (1977)
  17. ^ Справочное руководство по Free Pascal Language, раздел 3.4 Указатели
  18. ^ Контактные данные. «// Создание ссылок (ссылки на Perl и вложенные структуры данных)» . Perldoc.perl.org . Проверено 13 апреля 2018 .

Внешние ссылки [ править ]

  • Документ по обработке списков PL / I из июньского номера журнала CACM за 1967 г.
  • cdecl.org Инструмент для преобразования объявлений указателей в простой английский
  • Over IQ.com Руководство для начинающих с описанием указателей на простом английском языке.
  • Указатели и память: введение в указатели - Стэнфордская образовательная библиотека по информатике
  • Указатели в программировании на C Визуальная модель для новичков в программировании на C
  • 0pointer.de Краткий список исходных кодов минимальной длины, разыменовывающих нулевой указатель в нескольких разных языках программирования.
  • "The C book" - содержит примеры указателей в ANSI C
  • Объединенный технический комитет ISO / IEC JTC 1, Подкомитет SC 22, Рабочая группа WG 14 (2007-09-08). Международный стандарт ISO / IEC 9899 (PDF) . Проект комитета .CS1 maint: несколько имен: список авторов ( ссылка ).