Пожалуйста, включите пример с объяснением.
c++
c
pointers
dereference
Асир
источник
источник
int *p;
определил бы указатель на целое число и*p
разыменовал бы этот указатель, означая, что он фактически получит данные, на которые указывает p.Ответы:
Обзор основной терминологии
Это , как правило , достаточно хорошо - если вы не программируя сборки - предусмотреть указатель , содержащий числовой адрес памяти, с 1 со ссылку на вторые байты в памяти процесса, 2 третьих, четвертые 3 и так далее ....
Когда вы хотите получить доступ к данным / значению в памяти, на которые указывает указатель - к содержанию адреса с этим числовым индексом - тогда вы разыменовываете указатель.
Различные компьютерные языки имеют разные нотации, чтобы сообщить компилятору или интерпретатору, что вы сейчас заинтересованы в (текущем) значении объекта, на который указывает указатель - ниже я остановлюсь на C и C ++.
Сценарий указателя
Рассмотрим в C, учитывая указатель, такой как
p
ниже ...... четыре байта с числовыми значениями, используемыми для кодирования букв «a», «b», «c» и 0 байтов для обозначения конца текстовых данных, хранятся где-то в памяти, а числовой адрес этого данные хранятся в
p
. Таким образом, C кодирует текст в памяти, известный как ASCIIZ .Например, если строковый литерал оказался по адресу 0x1000, а
p
32-битный указатель - по адресу 0x2000, содержимое памяти будет:Обратите внимание , что не имя переменной / идентификатор адреса 0x1000, но мы можем косвенно ссылаться на строковый литерал с помощью указателя , хранящий его адрес:
p
.Разыменование указателя
Чтобы сослаться на символы
p
, на которые мы ссылаемся , мы разыменовываем,p
используя одно из этих обозначений (опять же, для C):Вы также можете перемещать указатели по указанным данным, разыменовывая их по мере продвижения:
Если у вас есть данные, в которые можно записать данные, вы можете сделать следующее:
Выше вы должны были знать, что во время компиляции вам понадобится переменная с именем
x
, и код просит компилятор указать, где она должна храниться, обеспечивая доступ к адресу через&x
.Разыменование и доступ к элементу данных структуры
В C, если у вас есть переменная, которая является указателем на структуру с элементами данных, вы можете получить доступ к этим элементам, используя
->
оператор разыменования:Многобайтовые типы данных
Чтобы использовать указатель, компьютерной программе также необходимо получить представление о типе данных, на которые он указывает - если для этого типа данных требуется более одного байта, то указатель обычно указывает на байт с наименьшим номером в данных.
Итак, рассмотрим чуть более сложный пример:
Указатели на динамически выделяемую память
Иногда вы не знаете, сколько памяти вам понадобится, пока ваша программа не запустится и не увидит, какие данные выбрасываются в нее ... тогда вы можете динамически распределять память, используя
malloc
. Это обычная практика хранить адрес в указателе ...В C ++ выделение памяти обычно выполняется с помощью
new
оператора, а освобождение с помощьюdelete
:См. Также умные указатели C ++ ниже.
Потеря и утечка адресов
Часто указатель может быть единственным указанием того, где некоторые данные или буфер существуют в памяти. Если требуется постоянное использование этих данных / буфера или возможность вызова
free()
илиdelete
предотвращения утечки памяти, то программист должен работать с копией указателя ...... или тщательно организовать аннулирование любых изменений ...
C ++ умные указатели
В C ++ рекомендуется использовать объекты интеллектуальных указателей для хранения и управления указателями, автоматически освобождая их при запуске деструкторов интеллектуальных указателей. Начиная с C ++ 11 стандартная библиотека предоставляет два,
unique_ptr
когда есть единственный владелец для выделенного объекта ...... и
shared_ptr
для владения акциями (с использованием подсчета ссылок ) ...Нулевые указатели
В C
NULL
и0
- и дополнительно в C ++nullptr
- может использоваться для указания того, что указатель в настоящее время не содержит адрес памяти переменной, и не должен разыменовываться или использоваться в арифметике указателей. Например:В C и C ++, так же , как встроенные числовые типы не обязательно по умолчанию
0
, ниbools
кfalse
, указатели не всегда имеет значениеNULL
. Все они устанавливаются в 0 / false / NULL, когда они являютсяstatic
переменными или (только C ++) прямыми или косвенными переменными-членами статических объектов или их баз, или подвергаются нулевой инициализации (например,new T();
иnew T(x, y, z);
выполняют нулевую инициализацию для членов T, включая указатели, тогда какnew T;
не).Кроме того, при назначении
0
,NULL
иnullptr
к указателю биты в указателе не обязательно сбрасываются: указатель может не содержать «0» на аппаратном уровне, или обратитесь к адресу 0 в вашем виртуальном адресном пространстве. Компилятор разрешено хранить что - то еще там , если у нее есть основания, но все , что он делает - если вы пришли вместе и сравнить указатель0
,NULL
,nullptr
или другой указатель , который был назначен какой - либо из тех, сравнение должны работать , как ожидалось. Итак, ниже исходного кода на уровне компилятора «NULL» потенциально немного «волшебен» в языках C и C ++ ...Подробнее об адресах памяти и почему вам, вероятно, не нужно знать
Точнее, инициализированные указатели хранят битовый шаблон, идентифицирующий либо адрес памяти
NULL
(часто виртуальный ).В простом случае это числовое смещение во всем виртуальном адресном пространстве процесса; в более сложных случаях указатель может относиться к некоторой конкретной области памяти, которую ЦП может выбирать на основе регистров «сегмента» ЦП или некоторого вида идентификатора сегмента, закодированного в битовой комбинации, и / или просматривая в разных местах в зависимости от инструкции машинного кода с использованием адреса.
Например,
int*
должным образом инициализированный указатель наint
переменную может - после преобразования вfloat*
- доступную память в памяти «GPU» совершенно отличаться от памяти, в которой находитсяint
переменная, затем один раз приведен и использован как указатель на функцию, которая может указывать на дальнейшее отдельные машинные коды операций для хранения памяти (с числовым значениемint*
фактически случайного, недействительного указателя в этих других областях памяти).Языки программирования 3GL, такие как C и C ++, как правило, скрывают эту сложность, так что:
Если компилятор дает вам указатель на переменную или функцию, вы можете разыменовать его свободно (если переменная не уничтожена / не удалена в это время), и проблема компилятора заключается в том, нужно ли, например, предварительно восстанавливать конкретный регистр сегмента ЦП, или используется отдельная инструкция машинного кода
Если вы получаете указатель на элемент в массиве, вы можете использовать арифметику указателей для перемещения в другое место в массиве или даже для формирования адреса один за другим в конце массива, что допустимо для сравнения с другими указателями на элементы в массиве (или которые были аналогично перемещены арифметикой указателя к тому же значению «один за другим»); опять же в C и C ++, это зависит от компилятора, чтобы убедиться, что это "просто работает"
Определенные функции ОС, например отображение общей памяти, могут дать вам указатели, и они будут «просто работать» в пределах диапазона адресов, который имеет для них смысл
Попытки переместить законные указатели за эти границы, или привести произвольные числа к указателям, или использовать указатели, приведенные к несвязанным типам, обычно имеют неопределенное поведение , поэтому их следует избегать в библиотеках и приложениях более высокого уровня, но код для ОС, драйверы устройств и т. Д. Возможно, потребуется полагаться на поведение, оставленное неопределенным в стандарте C или C ++, что, тем не менее, хорошо определяется их конкретной реализацией или оборудованием.
источник
p[1]
и*(p + 1)
одинаковые ? То есть лиp[1]
и*(p + 1)
генерировать те же инструкции?p
равен всего 2000: если бы у вас был другой указатель наp
него, пришлось бы хранить 2000 в его четырех или восьми байтах. Надеюсь, это поможет! Приветствия.u
содержит массивarr
, и gcc, и clang распознают, что lvalueu.arr[i]
может обращаться к тому же хранилищу, что и другие члены объединения, но не распознают, что lvalue*(u.arr+i)
может это делать. Я не уверен, считают ли авторы этих компиляторов, что последний вызывает UB, или что первый вызывает UB, но они все равно должны его обработать с пользой, но они явно рассматривают два выражения как разные.Разыменование указателя означает получение значения, которое хранится в ячейке памяти, указанной указателем. Оператор * используется для этого и называется оператором разыменования.
источник
[]
также разыменовывает указатель (a[b]
определяется как среднее*(a + b)
).Указатель - это «ссылка» на значение ... так же, как номер вызова библиотеки - это ссылка на книгу. «Разыменование» номера вызова физически проходит и извлекает эту книгу.
Если книги нет, библиотекарь начинает кричать, закрывает библиотеку, и несколько человек собираются выяснить причину, по которой человек найдет книгу, которой там нет.
источник
Проще говоря, разыменование означает доступ к значению из определенной области памяти, на которую указывает этот указатель.
источник
Код и объяснение из Основы указателя :
источник
Я думаю, что все предыдущие ответы неверны, поскольку они утверждают, что разыменование означает доступ к фактическому значению. Вместо этого Википедия дает правильное определение: https://en.wikipedia.org/wiki/Dereference_operator
Тем не менее, мы можем разыменовать указатель, не обращаясь к значению, на которое он указывает. Например:
Мы разыменовали указатель NULL без доступа к его значению. Или мы могли бы сделать:
Опять же, разыменование, но никогда не доступ к значению. Такой код НЕ вылетает: сбой происходит, когда вы фактически получаете доступ к данным по недействительному указателю. Однако, к сожалению, согласно стандарту, разыменование недействительного указателя является неопределенным поведением (за некоторыми исключениями), даже если вы не пытаетесь прикоснуться к фактическим данным.
Короче говоря: разыменование указателя означает применение к нему оператора разыменования. Этот оператор просто возвращает l-значение для вашего будущего использования.
источник
*p;
вызывает неопределенное поведение. Хотя вы правы, что разыменование не имеет доступа к значению как таковому , код*p;
действительно получает доступ к значению.