~~~~~~~~~~~~~~~~~~~~ Arc.Ask3.Ru ~~~~~~~~~~~~~~~~~~~~~ 
Номер скриншота №:
✰ 21E75B7CC2599C5339BE4BC95E8AD958__1717445940 ✰
Заголовок документа оригинал.:
✰ Pointer (computer programming) - Wikipedia ✰
Заголовок документа перевод.:
✰ Указатель (компьютерное программирование) — Википедия ✰
Снимок документа находящегося по адресу (URL):
✰ https://en.wikipedia.org/wiki/Pointer_(computer_programming) ✰
Адрес хранения снимка оригинал (URL):
✰ https://arc.ask3.ru/arc/aa/21/58/21e75b7cc2599c5339be4bc95e8ad958.html ✰
Адрес хранения снимка перевод (URL):
✰ https://arc.ask3.ru/arc/aa/21/58/21e75b7cc2599c5339be4bc95e8ad958__translat.html ✰
Дата и время сохранения документа:
✰ 20.06.2024 02:47:11 (GMT+3, MSK) ✰
Дата и время изменения документа (по данным источника):
✰ 3 June 2024, at 23:19 (UTC). ✰ 

~~~~~~~~~~~~~~~~~~~~~~ Ask3.Ru ~~~~~~~~~~~~~~~~~~~~~~ 
Сервисы Ask3.ru: 
 Архив документов (Снимки документов, в формате HTML, PDF, PNG - подписанные ЭЦП, доказывающие существование документа в момент подписи. Перевод сохраненных документов на русский язык.)https://arc.ask3.ruОтветы на вопросы (Сервис ответов на вопросы, в основном, научной направленности)https://ask3.ru/answer2questionТоварный сопоставитель (Сервис сравнения и выбора товаров) ✰✰
✰ https://ask3.ru/product2collationПартнерыhttps://comrades.ask3.ru


Совет. Чтобы искать на странице, нажмите Ctrl+F или ⌘-F (для MacOS) и введите запрос в поле поиска.
Arc.Ask3.ru: далее начало оригинального документа

Указатель (компьютерное программирование) — Википедия Jump to content

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

Из Википедии, бесплатной энциклопедии

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

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

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

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

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

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

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

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

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

Формальное описание [ править ]

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Использует [ править ]

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

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

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

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

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

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

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

интервал   *  ПТР  ; 

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

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

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

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

интервал   *  ptr   =   NULL  ; 

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

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

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

интервал   а   =   5  ; 
  интервал   *  ptr   =   NULL  ; 

  ПТР   =   a   ; 

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

*  ПТР   =   8  ; 

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

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

интервал   а   =   5  ; 
  интервал   *  ptr   =   NULL  ; 
Адрес Содержание
0x8130 0x00000005
0x8134 0x00000000

(Здесь показан NULL-указатель 0x00000000.) Присвоив адрес a к ptr:

ПТР   =   a   ; 

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

Адрес Содержание
0x8130 0x00000005
0x8134 0x00008130

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

*  ПТР   =   8  ; 

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

Адрес Содержание
0x8130 0x00000008
0x8134 0x00008130

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

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

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

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

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

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

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

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

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

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

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

int   массив  [  5  ]   =   {  2  ,   4  ,   3  ,   1  ,   5  }; 

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

0 1 2 3
1000 2 0 0 0
1004 4 0 0 0
1008 3 0 0 0
100С 1 0 0 0
1010 5 0 0 0

Здесь представлены пять целых чисел: 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 NULL 

 struct   link   { 
     void          *  data  ;     /* данные этой ссылки */ 
     struct   link   *  next  ;     /* следующая ссылка;   EMPTY_LIST, если его нет */ 
 }; 

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

  на данные  Ссылка   a   =   Nil 
              |    Минусы   и   (  Ссылка   и  ) 

Nil пустой список, и Cons a (Link a) это минус- ячейка типа 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 

     return   0  ; 
  } 

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

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

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

Пример кода C ниже иллюстрирует, как объекты структуры динамически выделяются и на них ссылаются. Стандартная библиотека 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  )); 
      if   (  item   ==   NULL  ) 
         вернуть   NULL  ; 

      /* Инициализируем члены нового элемента */ 
     memset  (  item  ,   0  ,   sizeof  (  struct   Item  )); 
      элемент  ->  идентификатор   =     -1  ; 
      элемент  ->  имя   =   NULL  ; 
      предмет  ->  стоимость   =   0,0  ; 

      /* Сохраняем копию имени в новом элементе */ 
     item  ->  name   =   malloc  (  strlen  (  name  )   +   1  ); 
      if   (  item  ->  name   ==   NULL  )   { 
         free  (  item  ); 
          вернуть   НУЛЬ  ; 
      } 
     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-битных целочисленных значений. Каждое значение состояло из кода ASCII в младшем байте и цвета в старшем байте. Таким образом, чтобы поместить букву «А» в строке 5 столбца 2 ярко-белым цветом на синем, нужно было бы написать следующий код:

#define VID ((unsigned short (*)[80])0xB8000) 

 void   foo  (  void  )   { 
     VID  [  4  ][  1  ]   =   0x1F00   |    'А'  ; 
  } 

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

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

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

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

Например, в С

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

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

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

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

сумки   =   (  символ   *  )  деньги  ; 

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

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

char   *  external_buffer   =   "abcdef"  ; 
  интервал   *  внутренние_данные  ; 

  внутренние_данные   =   (  int   *  )  внешний_буфер  ;     // НЕОПРЕДЕЛЕННОЕ ПОВЕДЕНИЕ, если «результирующий указатель 
                                          // неправильно выровнен» 

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

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

Значение указателей [ править ]

В C и C++ даже если два указателя сравниваются как равные, это не означает, что они эквивалентны. В этих языках и LLVM это правило интерпретируется как означающее, что «тот факт, что два указателя указывают на один и тот же адрес, не означает, что они равны в том смысле, что их можно использовать взаимозаменяемо», разница между указателями называется их происхождение . [10] Приведение к целочисленному типу, например uintptr_tопределяется реализацией, и сравнение, которое оно обеспечивает, не дает большего понимания того, являются ли эти два указателя взаимозаменяемыми. Кроме того, дальнейшее преобразование в байты и арифметику отпугнет оптимизаторов, пытающихся отслеживать использование указателей, и эта проблема все еще выясняется в академических исследованиях. [11]

Делаем указатели безопаснее [ править ]

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

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

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

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

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

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

Специальные виды указателей [ править ]

Виды, определяемые значением [ править ]

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

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

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

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

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

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

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

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

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

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

Виды, определяемые структурой [ править ]

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

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

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

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

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

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

Виды, определяемые использованием или типом данных [ править ]

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

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

 структуры  элемент   { 
     структуры   элемент   *  следующий  ; 
      целое              значение  ; 
  }; 

 структуры    элемент   *  head   =   NULL  ; 

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

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

 // Вызывающий абонент делает это: 
 вставить  (  &  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  ; 
      интервал   (  *  fp  )(  интервал  ,   интервал  );       // Указатель функции, который может указывать на такую ​​функцию, как sum 
     fp   =   &  sum  ;                 // fp теперь указывает на функцию sum 
     x   =   (  *  fp  )(  a  ,   b  );           // Вызывает функцию sum с аргументами a и b 
     y   =   sum  (  a  ,   b  );             // Вызывает функцию sum с аргументами a и b 
 } 

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

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

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

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

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

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

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

Есть [ править ]

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

БАЗОВЫЙ [ править ]

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

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

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

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

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

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

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

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

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

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

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

В C++ можно определить указатели на нестатические члены класса. Если класс C имеет члена T a затем &C::a является указателем на член a типа T C::*. Этот член может быть объектом или функцией . [16] Их можно использовать справа от операторов. .* и ->* для доступа к соответствующему члену.

структура   S   { 
 интервал   а  ; 
  int   f  ()   const   {  return   a  ;} 
 }; 
  С   с1  {}; 
  S  *   ptrS   =   &  s1  ; 

  int   S  ::*   ptr   =   &  S  ::  a  ;    // указатель на S::a 
 int   (  S  ::*   fp  )()  const   =   &  S  ::  f  ;    // указатель на S::f 

 s1  .   *  ПТР   =   1  ; 
  std  ::  cout   <<   (  s1  .  *  fp  )()   <<   "  \n  "  ;    // печатает 1 
 ptrS  ->*  ptr   =   2  ; 
  std  ::  cout   <<   (  ptrS  ->*  fp  )()   <<   "  \n  "  ;    // печатает 2 

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

Эти объявления указателей охватывают большинство вариантов объявлений указателей. Конечно, возможно иметь тройные указатели, но основные принципы тройного указателя уже существуют в двойном указателе. Используемое здесь именование соответствует выражению typeid(type).name() равно для каждого из этих типов при использовании g++ или clang . [17] [18]

символ   A5_A5_c   [  5  ][  5  ];      /* массив массивов символов */ 
 char   *  A5_Pc   [  5  ];          /* массив указателей на символы */ 
 char   **  PPc  ;               /* указатель на указатель на char («двойной указатель») */ 
 char   (  *  PA5_c  )   [  5  ];        /* указатель на массив(ы) символов */ 
 char   *  FPcvE  ();            /* функция, возвращающая указатель на символы char */ 
 char   (  *  PFcvE  )();          /* указатель на функцию, которая возвращает символ */ 
 char   (  *  FPA5_cvE  ())[  5  ];    /* функция, возвращающая указатель на массив символов */ 
 char   (  *  A5_PFcvE  [  5  ])();    /* массив указателей на функции, возвращающие символ */ 

Следующие объявления, включающие указатели на члены, допустимы только в C++:

класс   С  ; 
  класс   Д  ; 
  char   C  ::*   M1Cc  ;                 /* указатель на элемент char */ 
 char   C  ::*  A5_M1Cc   [  5  ];           /* массив указателей на член char */ 
 char  *   C  ::*   M1CPc  ;               /* указатель на член для указателя на символ(ы) */ 
 char   C  ::**   PM1Cc  ;               /* указатель на указатель на член на char */ 
 char   (  *  M1CA5_c  )   [  5  ];            /* указатель на член массива(ов) символов */ 
 char   C  ::*   FM1CcvE  ();            /* функция, которая возвращает указатель на элемент char */ 
 char   D  ::*   C  ::*   M1CM1Dc  ;         /* указатель на член на указатель на член на указатель на char(s) */ 
 char   C  ::*   C  ::*   M1CMS_c  ;         /* указатель на член на указатель на член на указатель на char(s) */ 
 char   (  C  ::*   FM1CA5_cvE  ())[  5  ];    /* функция, которая возвращает указатель на элемент массива символов */ 
 char   (  C  ::*   M1CFcvE  )()          /* функция-указатель на элемент, которая возвращает символ */ 
 char   (  C  ::*   A5_M1CFcvE  [  5  ])();    /* массив указателей на функции-члены, которые возвращают символ */ 

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

С# [ править ]

В языке программирования C# указатели поддерживаются либо путем маркировки блоков кода, содержащих указатели, с помощью unsafe ключевое слово или по using тот System.Runtime.CompilerServicesположения сборки для доступа к указателю. Синтаксис по существу такой же, как и в C++, а указанный адрес может быть либо управляемой , либо неуправляемой памятью. Однако указатели на управляемую память (любой указатель на управляемый объект) должны быть объявлены с использованием метода fixed ключевое слово, которое не позволяет сборщику мусора перемещать указанный объект в рамках управления памятью, пока указатель находится в области видимости, тем самым сохраняя действительный адрес указателя.

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

// Получаем 16 байт памяти из неуправляемой памяти процесса 
 IntPtr   указатель   =   System  .   Время выполнения  .   ИнтеропСервисы  .   Маршал  .   АллокХГлобал  (  16  ); 

  // Сделайте что-нибудь с выделенной памятью 

 // Освободите выделенную память 
 System  .   Время выполнения  .   ИнтеропСервисы  .   Маршал  .   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 не являются типизированными, поэтому для разыменования или присвоения указателей приведение типов не требуется. Синтаксис объявления указателя: 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 является производным от C и C++, который полностью поддерживает указатели C и приведение типов C.

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

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

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

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

тип  real_list_t 
   реальный   ::   sample_data  (  100  ) 
   тип   (  real_list_t  ),   указатель   ::   next   =>   null   () 
 конца 

 тип   (  real_list_t  ),   цель   ::   my_real_list 
 тип   (  real_list_t  ),   указатель   ::   real_list_temp 

 real_list_temp   =>   my_real_list 
 do 
 read   (  1  ,  iostat  =  ioerr  )   real_list_temp  %  sample_data 
   if   (  ioerr   /=   0  )   выход 
 распределить   (  real_list_temp  %  next  ) 
   real_list_temp   =>   real_list_temp  %  next 
 end do 

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

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

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

Ява [ править ]

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

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

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

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

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

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

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

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

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

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

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

Примечания [ править ]

  1. ^ Некоторые компиляторы позволяют хранить адреса функций в указателях void. В стандарте C++ указано преобразование указателя функции в void*как условно поддерживаемая функция, а стандарт C гласит, что такие преобразования являются «общими расширениями». Это требуется для POSIX. функции dlsym. [15]

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

  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 . Проверено 13 апреля 2018 г. Указатель Гарольда Лоусона.
  3. ^ «Список наград Компьютерного общества IEEE» . Награды.computer.org. Архивировано из оригинала 22 марта 2011 г. Проверено 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. ^ Перейти обратно: а б Плаугер, Пи Джей ; Броди, Джим (1992). Справочник программиста C по стандартам ANSI и ISO . Редмонд, Вашингтон: Microsoft Press. стр. 108, 51 . ISBN  978-1-55615-359-4 . Тип массива не содержит дополнительных дыр, поскольку все остальные типы плотно упаковываются при составлении в массивы [на странице 51]
  9. ^ WG14 N1124 , C – Утвержденные стандарты: ISO/IEC 9899 – Языки программирования – C , 06 мая 2005 г.
  10. ^ Юнг, Ральф. «Указатели сложны II, или: Нам нужны лучшие языковые спецификации» .
  11. ^ Юнг, Ральф. «Указатели сложны, или: что содержится в байте?» .
  12. ^ Патент США 6625718 , Штайнер, Роберт К. (Брумфилд, Колорадо), «Указатели, которые относятся к их собственному текущему местоположению», выдан 23 сентября 2003 г., передан Avaya Technology Corp. (Баскин-Ридж, Нью-Джерси).  
  13. ^ Патент США 6115721 , Надь, Майкл (Тампа, Флорида), «Система и метод сохранения и восстановления базы данных с использованием самоуказателей», выдан 5 сентября 2000 г., передан IBM (Армонк, Нью-Йорк).  
  14. ^ «Базовые указатели» . Msdn.microsoft.com . Проверено 13 апреля 2018 г.
  15. ^ «Выпуск 195 CWG» . cplusplus.github.io . Проверено 15 февраля 2024 г.
  16. ^ «Указатели на функции-члены» . isocpp.org . Проверено 26 ноября 2022 г.
  17. ^ «c++filt(1) — справочная страница Linux» .
  18. ^ «Итаниум C++ ABI» .
  19. ^ Ульф Билтинг, Ян Скансхольм, «Vägen до C» (Дорога к C), третье издание, стр. 169, ISBN   91-44-01468-6
  20. ^ Ник Парланте, [1] , Стэнфордская образовательная библиотека по информатике , стр. 9–10 (2000).
  21. ^ Стандарт ISO 7185 Pascal (неофициальная копия), раздел 6.4.4 Типы указателей. Архивировано 24 апреля 2017 г. на Wayback Machine и позднее.
  22. ^ Дж. Уэлш, У. Дж. Снерингер и К. А. Хоар, «Двусмысленность и неуверенность в Паскале», Программное обеспечение: Практика и опыт 7 , стр. 685–696 (1977).
  23. ^ Справочник по бесплатному языку Pascal, раздел 3.4 Указатели
  24. ^ Контактная информация. "// Создание ссылок (ссылки Perl и вложенные структуры данных)" . Perldoc.perl.org . Проверено 13 апреля 2018 г.

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

Arc.Ask3.Ru: конец оригинального документа.
Arc.Ask3.Ru
Номер скриншота №: 21E75B7CC2599C5339BE4BC95E8AD958__1717445940
URL1:https://en.wikipedia.org/wiki/Pointer_(computer_programming)
Заголовок, (Title) документа по адресу, URL1:
Pointer (computer programming) - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть, любые претензии не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, денежную единицу можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)