В авторитетном источнике о C, после обсуждения &
оператора дается следующая информация :
... Немного прискорбно, что терминология [адрес] остается, потому что она сбивает с толку тех, кто не знает, о чем идет речь, и вводит в заблуждение тех, кто это делает: думать о указателях так, как будто они были адресами, обычно приводит к горе ... ,
Другие материалы, которые я читал (я бы сказал, из столь же авторитетных источников), всегда безоговорочно называли указатели и &
операторы как адреса памяти. Я хотел бы продолжать искать актуальность этого вопроса, но это довольно сложно, когда авторитетные источники KIND OF не согласны.
Теперь я немного растерялся - что же это за указатель, если не адрес памяти?
PS
Позже автор говорит: ... Я буду продолжать использовать термин «адрес», потому что изобрести другой [термин] было бы еще хуже.
источник
Ответы:
Стандарт C не определяет, что такое указатель внутри и как он работает внутри. Это сделано намеренно, чтобы не ограничивать количество платформ, где C может быть реализован как скомпилированный или интерпретируемый язык.
Значение указателя может быть своего рода идентификатором или дескриптором или комбинацией нескольких идентификаторов (например, привет сегментам и смещениям x86) и необязательно реальным адресом памяти. Этот идентификатор может быть чем угодно, даже текстовой строкой фиксированного размера. Неадресные представления могут быть особенно полезны для интерпретатора языка C.
источник
Я не уверен в вашем источнике, но тип языка, который вы описываете, исходит из стандарта C:
Так что ... да, указатели указывают на адреса памяти. По крайней мере, так говорит стандарт С.
Проще говоря, указатель - это переменная, содержащая значение некоторого адреса . Адрес объекта (который может храниться в указателе) возвращается с унарным
&
оператором.Я могу сохранить адрес «42 Wallaby Way, Sydney» в переменной (и эта переменная будет своего рода «указателем», но, поскольку это не адрес памяти, мы не будем называть его «указателем»). Ваш компьютер имеет адреса для своих блоков памяти. Указатели хранят значение адреса (то есть указатель хранит значение «42 Wallaby Way, Sydney», которое является адресом).
Редактировать: Я хочу расширить комментарий Алексея Фрунзе.
Что именно является указателем? Давайте посмотрим на стандарт C:
По сути, указатели хранят значение, которое предоставляет ссылку на некоторый объект или функцию. Вид. Указатели предназначены для хранения значения, которое предоставляет ссылку на некоторый объект или функцию, но это не всегда так:
Приведенная выше цитата говорит, что мы можем превратить целое число в указатель. Если мы сделаем это (то есть, если мы вставим целочисленное значение в указатель вместо конкретной ссылки на объект или функцию), то указатель «может не указывать на объект ссылочного типа» (т. Е. Он может не обеспечивать ссылка на объект или функцию). Это может дать нам что-то еще. И это единственное место, где вы можете вставить какой-то дескриптор или идентификатор в указатель (то есть указатель не указывает на объект; он хранит значение, которое представляет что-то, но это значение может не быть адресом).
Так что да, как говорит Алексей Фрунзе, возможно, указатель не хранит адрес объекта или функции. Возможно, вместо этого указатель хранит некоторый «дескриптор» или идентификатор, и вы можете сделать это, присвоив указателю какое-то произвольное целочисленное значение. То, что представляет этот дескриптор или идентификатор, зависит от системы / среды / контекста. Пока ваша система / реализация может понять значение, вы в хорошей форме (но это зависит от конкретного значения и конкретной системы / реализации).
Обычно указатель хранит адрес объекта или функции. Если он не хранит фактический адрес (для объекта или функции), результат определяется реализацией (это означает, что именно то, что происходит и то, что теперь представляет указатель, зависит от вашей системы и реализации, поэтому это может быть дескриптор или идентификатор для конкретной системы, но использование того же кода / значения в другой системе может привести к сбою вашей программы).
Это оказалось дольше, чем я думал, что это будет ...
источник
В этой картине,
pointer_p - это указатель, который расположен в 0x12345 и указывает на переменную variable_v в 0x34567.
источник
Думать о указателе как об адресе - это приближение . Как и во всех приближениях, иногда это достаточно полезно, но также не совсем точно, что означает, что полагаться на него вызывает проблемы.
Указатель похож на адрес в том смысле, что он указывает, где найти объект. Одно непосредственное ограничение этой аналогии - то, что не все указатели фактически содержат адрес.
NULL
это указатель, который не является адресом Содержимое переменной-указателя фактически может быть одного из трех видов:p
содержит адрес ,x
то выражение*p
имеет такое же значение , какx
);NULL
в качестве примера;p
оно не содержит допустимого значения, то*p
может сделать что-либо («неопределенное поведение»), с довольно частой возможностью сбоя программы).Более того, было бы точнее сказать, что указатель (если он действителен и не равен нулю) содержит адрес: указатель указывает, где найти объект, но с ним связано больше информации.
В частности, указатель имеет тип. На большинстве платформ тип указателя не имеет влияния во время выполнения, но он влияет не только на тип во время компиляции. Если
p
указатель наint
(int *p;
), тоp + 1
указывает на целое число, которое являетсяsizeof(int)
байтами послеp
(при условии, чтоp + 1
это все еще допустимый указатель). Еслиq
указательchar
указывает на тот же адрес, что иp
(char *q = p;
), тоq + 1
это не тот же адрес, что иp + 1
. Если вы рассматриваете указатель как адрес, не очень интуитивно понятно, что «следующий адрес» различен для разных указателей на одно и то же местоположение.В некоторых средах возможно иметь несколько значений указателей с разными представлениями (разными битовыми комбинациями в памяти), которые указывают на одно и то же место в памяти. Вы можете рассматривать их как разные указатели, содержащие один и тот же адрес, или как разные адреса для одного и того же местоположения - в этом случае метафора не ясна.
==
Оператор всегда говорит вам ли два операнда указывают на то же место, так что на этих условиях вы можете иметь ,p == q
хотяp
иq
имеют различные битовые шаблоны.Существуют даже среды, в которых указатели переносят другую информацию за пределы адреса, такую как информация о типе или разрешении. Вы можете легко пройти свою жизнь как программист, не сталкиваясь с этим.
Существуют среды, в которых разные виды указателей имеют разные представления. Вы можете думать об этом как о разных видах адресов, имеющих разные представления. Например, некоторые архитектуры имеют байтовые указатели и указатели слов или указатели объектов и указатели функций.
В целом, думать об указателях как об адресах не так уж и плохо, если учесть, что
Обратный путь намного более хлопотный. Не все, что выглядит как адрес, может быть указателем . Где-то глубоко внутри любой указатель представлен в виде битового шаблона, который можно прочитать как целое число, и вы можете сказать, что это целое число является адресом. Но если пойти по другому пути, не каждое целое число является указателем.
Сначала есть некоторые известные ограничения; например, целое число, обозначающее местоположение вне адресного пространства вашей программы, не может быть допустимым указателем. Неверно выровненный адрес не делает действительный указатель для типа данных, который требует выравнивания; например, на платформе, где
int
требуется 4-байтовое выравнивание, 0x7654321 не может быть допустимымint*
значением.Однако это выходит далеко за рамки этого, потому что когда вы превращаете указатель в целое число, вы попадаете в мир неприятностей. Большая часть этой проблемы заключается в том, что оптимизирующие компиляторы гораздо лучше справляются с микрооптимизацией, чем большинство программистов ожидают, поэтому их ментальная модель работы программы глубоко неверна. То, что у вас есть указатели с одинаковым адресом, не означает, что они эквивалентны. Например, рассмотрим следующий фрагмент:
Вы можете ожидать, что на обычной машине, где
sizeof(int)==4
иsizeof(short)==2
, это либо печатает1 = 1?
(little-endian), либо65536 = 1?
(big-endian). Но на моем 64-битном ПК с Linux с GCC 4.4:GCC достаточно любезен, чтобы предупредить нас о том, что идет не так в этом простом примере - в более сложных примерах компилятор может не заметить. Так как
p
имеет другой тип&x
, то изменение того, на чтоp
указывает, не может повлиять на то, на что&x
указывает (за исключением некоторых четко определенных исключений). Поэтому компилятор может сохранять значениеx
в регистре и не обновлять этот регистр как*p
изменения. Программа разыменовывает два указателя на один и тот же адрес и получает два разных значения!Мораль этого примера заключается в том, что думать о (ненулевом действительном) указателе как об адресе хорошо, если вы придерживаетесь точных правил языка Си. Обратная сторона медали в том, что правила языка Си являются сложными, и их трудно понять интуитивно, если вы не знаете, что происходит под капотом. И то, что происходит под капотом, заключается в том, что связь между указателями и адресами несколько ослабла, как для поддержки «экзотических» архитектур процессоров, так и для поддержки оптимизирующих компиляторов.
Поэтому думайте, что указатели являются адресами, как первый шаг в вашем понимании, но не следуйте этой интуиции слишком далеко.
источник
*p = 3
гарантированно будет успешным, когда p не был инициализирован.NULL
нет, но для уровня детализации, требуемого здесь, это не имеет значения, отвлекает. Даже для повседневного программирования тот факт, что онNULL
может быть реализован как нечто, неNULL
говорящее «указатель», встречается не часто (прежде всего, переходя к функции с переменным числом аргументов - но даже там, если вы не приводите ее) вы уже предполагаете, что все типы указателей имеют одинаковое представление).Указатель - это переменная, которая хранит адрес памяти, а не сам адрес. Однако вы можете разыменовать указатель - и получить доступ к ячейке памяти.
Например:
Вот и все. Это так просто.
Программа для демонстрации того, что я говорю, и ее выход здесь:
http://ideone.com/rcSUsb
Программа:
источник
fopen
в переменную только в том случае, если вам нужно использовать его более одного раза (что, кfopen
примеру, почти всегда).Трудно сказать, что именно имеют в виду авторы этих книг. Содержит ли указатель адрес или нет, зависит от того, как вы определяете адрес и как вы определяете указатель.
Судя по всем написанным ответам, некоторые люди предполагают, что (1) адрес должен быть целым числом и (2) указатель не обязательно должен быть виртуальным, чтобы его не было сказано в спецификации. С этими допущениями ясно, что указатели не обязательно содержат адреса.
Тем не менее, мы видим, что хотя (2), вероятно, верно, (1), вероятно, не обязательно должно быть верно. И что делать с тем фактом, что & называется адресом оператора согласно ответу @ CornStalks? Означает ли это, что авторы спецификации намереваются, чтобы указатель содержал адрес?
Можно ли сказать, что указатель содержит адрес, но адрес не обязательно должен быть целым числом? Может быть.
Я думаю, что все это - бредовой педантичный семантический разговор. Это абсолютно бесполезно практически говоря. Можете ли вы представить себе компилятор, который генерирует код таким образом, что значение указателя не является адресом? Если да, то? Это то, о чем я думал...
Я думаю, что автор книги (первая выдержка, утверждающая, что указатели не обязательно являются просто адресами), вероятно, ссылается на тот факт, что указатель сопровождается информацией о типе.
Например,
и y, и z являются указателями, но y + 1 и z + 1 различны. если они являются адресами памяти, разве эти выражения не дадут вам одинаковое значение?
И здесь во лжи размышления о указателях, как если бы они были адресами, обычно приводят к печали . Ошибки были написаны, потому что люди думают об указателях, как будто они были адресами , и это обычно приводит к горе .
55555, вероятно, не указатель, хотя это может быть адрес, но (int *) 55555 является указателем. 55555 + 1 = 55556, но (int *) 55555 + 1 составляет 55559 (+/- разница в размере size (int)).
источник
far
указатель не просто "целое число".Ну, указатель - это абстракция, представляющая область памяти. Обратите внимание, что цитата не говорит, что думать о указателях, как будто они были адресами памяти, неверна, она просто говорит, что «обычно приводит к горе». Другими словами, это приводит к неверным ожиданиям.
Наиболее вероятным источником горя, безусловно, является арифметика указателей, которая на самом деле является одной из сильных сторон Си. Если указатель был адресом, можно ожидать, что арифметика указателя будет арифметикой адреса; но это не так. Например, добавление 10 к адресу должно дать вам адрес, который больше на 10 единиц адресации; но добавление 10 к указателю увеличивает его в 10 раз по сравнению с типом объекта, на который он указывает (и даже не фактическим размером, а округленным до границы выравнивания). При использовании
int *
обычной архитектуры с 32-разрядными целыми числами добавление к ней 10 увеличит ее на 40 единиц адресации (байтов). Опытные программисты на C знают об этом и живут этим, но ваш автор, очевидно, не фанат небрежных метафор.Есть дополнительный вопрос о том, как содержимое указателя представляет ячейку памяти: как объяснили многие ответы, адрес не всегда является целым (или длинным). В некоторых архитектурах адрес - это «сегмент» плюс смещение. Указатель может даже содержать только смещение в текущем сегменте («ближний» указатель), которое само по себе не является уникальным адресом памяти. И содержимое указателя может иметь только косвенную связь с адресом памяти, как его понимает аппаратное обеспечение. Но автор цитируемой цитаты даже не упоминает репрезентацию, поэтому я думаю, что они имели в виду концептуальную эквивалентность, а не репрезентацию.
источник
Вот как я объяснил это некоторым сбитым с толку людям в прошлом: указатель имеет два атрибута, которые влияют на его поведение. У него есть значение , которое является (в типичных средах) адресом памяти и типом , который сообщает вам тип и размер объекта, на который он указывает.
Например, учитывая:
Вы можете иметь три разных указателя, указывающих на один и тот же объект:
Если вы сравните значения этих указателей, они все равны:
Однако, если вы увеличите каждый указатель, вы увидите, что тип, на который они указывают, становится релевантным.
Переменные
i
иc
будут иметь разные значения в этой точке, потому чтоi++
заставляетi
содержать адрес следующего доступного целого числа иc++
заставляетc
указывать на следующий адресуемый символ. Как правило, целые числа занимают больше памяти, чем символы, поэтому вi
итоге получим большее значение, чемc
после того, как они оба будут увеличены.источник
i == c
плохо сформирован (вы можете сравнивать указатели на разные типы только при неявном преобразовании из одного в другой). Кроме того, исправление этого с помощью приведения означает, что вы применили преобразование, и тогда остается дискуссионным, изменяет ли преобразование значение или нет. (Вы можете утверждать, что это не так, но тогда это просто утверждение того же, что вы пытались доказать с помощью этого примера).Марк Бесси уже сказал это, но это нужно еще раз подчеркнуть, пока не поймут.
Указатель имеет такое же отношение к переменной, как литерал 3.
Указатель - это кортеж значения (адреса) и типа (с дополнительными свойствами, такими как только чтение). Тип (и дополнительные параметры, если таковые имеются) могут дополнительно определять или ограничивать контекст; например.
__far ptr, __near ptr
: каков контекст адреса: стек, куча, линейный адрес, смещение откуда-то, физическая память или что.Это свойство типа, которое делает арифметику указателей немного отличной от целочисленной арифметики.
Счетчик примеров того, что указатель не является переменной, слишком велик, чтобы его игнорировать
fopen возвращает указатель FILE. (где переменная)
указатель стека или указатель кадра обычно являются неадресуемыми регистрами
*(int *)0x1231330 = 13;
- приведение произвольного целочисленного значения к типу pointer_of_integer и запись / чтение целого числа, даже не вводя переменнуюВо время жизни C-программы будет много других экземпляров временных указателей, которые не имеют адресов - и, следовательно, они не являются переменными, а выражениями / значениями с типом, связанным со временем компиляции.
источник
Вы правы и вменяемы. Обычно указатель - это просто адрес, поэтому вы можете привести его к целому числу и выполнить любую арифметику.
Но иногда указатели являются только частью адреса. На некоторых архитектурах указатель преобразуется в адрес с добавлением базы или используется другой регистр ЦП .
Но в наши дни на ПК и архитектуре ARM с плоской моделью памяти и языком C, скомпилированным с нуля, нормально думать, что указатель является целочисленным адресом в некотором месте в одномерной адресуемой ОЗУ.
источник
Указатель, как и любая другая переменная в C, по сути является набором битов, которые могут быть представлены одним или несколькими объединенными
unsigned char
значениями (как и в любом другом типе cariable,sizeof(some_variable)
будет указывать количествоunsigned char
значений). Что отличает указатель от других переменных, так это то, что компилятор C будет интерпретировать биты в указателе как идентифицирующие место, где может храниться переменная. В C, в отличие от некоторых других языков, можно запросить пространство для нескольких переменных, а затем преобразовать указатель на любое значение в этом наборе в указатель на любую другую переменную в этом наборе.Многие компиляторы реализуют указатели, используя свои биты, хранящие фактические машинные адреса, но это не единственно возможная реализация. Реализация могла бы сохранить один массив - недоступный для кода пользователя - перечисляющий аппаратный адрес и выделенный размер всех объектов памяти (наборов переменных), которые использовала программа, и каждый указатель содержал бы индекс в массив вдоль со смещением от этого индекса. Такая конструкция позволила бы системе не только ограничивать работу кода только над памятью, которой она владела, но и гарантировать, что указатель на один элемент памяти не может быть случайно преобразован в указатель на другой элемент памяти (в системе, использующей аппаратное обеспечение). адреса, если
foo
иbar
являются массивами из 10 элементов, которые хранятся последовательно в памяти, указатель на «одиннадцатый» элементfoo
вместо этого может указывать на первый элементbar
, но в системе, где каждый «указатель» является идентификатором объекта и смещением, система может перехватить ловушку, если код попытается проиндексировать указатель заfoo
пределами своего выделенного диапазона). Такая система также могла бы устранить проблемы фрагментации памяти, поскольку физические адреса, связанные с любыми указателями, можно перемещать.Обратите внимание, что, хотя указатели несколько абстрактны, они недостаточно абстрактны, чтобы компилятор C, полностью соответствующий стандартам, мог реализовать сборщик мусора. Компилятор C указывает, что каждая переменная, включая указатели, представлена в виде последовательности
unsigned char
значений. Для любой переменной можно разложить ее на последовательность чисел, а затем преобразовать эту последовательность чисел обратно в переменную исходного типа. Следовательно, программа могла быcalloc
некоторое хранилище (получая указатель на него), сохраняйте что-то там, разбивайте указатель на серию байтов, отображайте их на экране и затем стирайте все ссылки на них. Если программа затем принимает некоторые цифры с клавиатуры, восстанавливает их в указателе, а затем пытается прочитать данные из этого указателя, и если пользователь вводит те же числа, которые ранее отображала программа, программа должна будет выводить данные который был сохранен вcalloc
памяти. Поскольку невозможно представить, чтобы компьютер мог узнать, сделал ли пользователь копию отображенных чисел, невозможно предположить, если бы компьютер мог знать, будет ли когда-либо доступ к вышеупомянутой памяти в будущем.источник
free
, конечно, не вызывается явно). Будет ли полученная реализация настолько полезной, это другой вопрос, так как ее способность собирать может быть слишком ограниченной, но вы можете, по крайней мере, назвать ее сборщиком мусора :-) Назначение указателя и арифметика не «утекут» значение, но любой доступ кchar*
неизвестному происхождению должен быть проверен.free
еще не был вызван, либо предотвращать превращение любой ссылки на освобожденный объект в ссылку на живой объект [даже при использовании ресурсов, которые требуют явное управление временем жизни, GC все еще может выполнять последнюю функцию]; система GC, которая иногда ошибочно рассматривает объекты как имеющие живые ссылки на них, может быть полезной, если вероятность того, что N объектов будет ненужно закреплено одновременно, приближается к нулю, когда N становится большим . Если только кто-то не захочетУказатель - это тип переменной, который изначально доступен в C / C ++ и содержит адрес памяти. Как и любая другая переменная, она имеет собственный адрес и занимает память (количество зависит от платформы).
Одна из проблем, которую вы увидите в результате путаницы, - попытка изменить референт внутри функции, просто передав указатель по значению. Это создаст копию указателя в области действия функции и любые изменения в том месте, где этот новый указатель «указывает», не изменит референта указателя в области действия, вызвавшей функцию. Чтобы изменить действительный указатель внутри функции, обычно нужно передать указатель на указатель.
источник
КРАТКОЕ РЕЗЮМЕ (которое я также поставлю вверху):
(0) Думать об указателях как адресах часто является хорошим инструментом обучения и часто является реальной реализацией указателей на обычные типы данных.
(1) Но на многих, возможно, большинстве, указатели компиляторов на функции не являются адресами, а больше, чем адрес (как правило, в 2 раза, иногда больше), или на самом деле являются указателями на структуру в памяти, которая содержит адреса функций и тому подобное постоянный пул.
(2) Указатели на элементы данных и указатели на методы часто даже более странны.
(3) Устаревший код x86 с проблемами указателей FAR и NEAR
(4) Несколько примеров, особенно IBM AS / 400, с безопасными «жирными указателями».
Я уверен, что вы можете найти больше.
ДЕТАЛЬ:
UMMPPHHH !!!!! Многие из ответов до сих пор являются довольно типичными ответами "программиста", но не "компилятора" или "аппаратного обеспечения". Так как я притворяюсь аппаратным и часто работаю с компилятором, позвольте мне добавить два моих цента:
На многих, возможно, на большинстве компиляторов Си указатель на данные типа
T
является, по сути, адресомT
.Хорошо.
Но даже на многих из этих компиляторов некоторые указатели НЕ являются адресами. Вы можете сказать это, посмотрев на
sizeof(ThePointer)
.Например, указатели на функции иногда намного больше, чем обычные адреса. Или они могут включать уровень косвенности. Эта статьяпредоставляет одно описание, включающее процессор Intel Itanium, но я видел другие. Как правило, для вызова функции вы должны знать не только адрес кода функции, но также адрес пула констант функции - область памяти, из которой константы загружаются с помощью одной инструкции загрузки, а не компилятор, который должен генерировать 64-битная константа из нескольких команд Load Immediate и Shift и OR. Таким образом, вместо одного 64-битного адреса, вам нужно 2 64-битных адреса. Некоторые ABI (двоичные интерфейсы приложений) перемещают это как 128 бит, тогда как другие используют уровень косвенности, причем указатель функции фактически является адресом дескриптора функции, который содержит 2 фактических адреса, которые только что упомянуты. Что лучше? Зависит от вашей точки зрения: производительность, размер кода, и некоторые проблемы совместимости - часто код предполагает, что указатель может быть приведен к длинному или длинному длинному, но может также предполагать, что длинный длинный составляет ровно 64 бита. Такой код может не соответствовать стандартам, но, тем не менее, клиенты могут захотеть, чтобы он работал.
У многих из нас остались болезненные воспоминания о старой сегментированной архитектуре Intel x86 с БЛИЖАЙШИМИ И ДАЛЬНЕЙШИМИ указателями. К счастью, они почти вымерли, так что только краткое резюме: в 16-битном реальном режиме реальный линейный адрес был
В то время как в защищенном режиме это может быть
с результирующим адресом, проверяемым по ограничению, установленному в сегменте. Некоторые программы использовали не совсем стандартные объявления указателей FAR и NEAR в C / C ++, но многие только что сказали
*T
- но были переключатели компилятора и компоновщика, так что, например, указатели кода могли быть рядом с указателями, просто 32-битное смещение относительно того, что находится в регистр CS (кодовый сегмент), в то время как указатели данных могут быть указателями FAR, указывая как 16-битный номер сегмента, так и 32-битное смещение для 48-битного значения. Теперь, оба эти количества, безусловно, связаны с адресом, но, поскольку они не одинакового размера, какой из них является адресом? Кроме того, сегменты также имели разрешения - только чтение, чтение-запись, исполняемый файл - в дополнение к материалам, связанным с фактическим адресом.Более интересным примером, IMHO, является (или, возможно, был) семейство IBM AS / 400. Этот компьютер был одним из первых, кто внедрил ОС на C ++. Указатели на этом механизме обычно в 2 раза превышают реальный размер адреса - например, в этой презентацииговорит, 128-битные указатели, но фактические адреса были 48-64 бит, и, опять же, некоторая дополнительная информация, которая называется возможностью, которая предоставляла разрешения, такие как чтение, запись, а также ограничение для предотвращения переполнения буфера. Да, вы можете сделать это совместимо с C / C ++ - и если бы это было повсеместно, китайская PLA и славянская мафия не взломали бы так много западных компьютерных систем. Но исторически большинство программирования на C / C ++ пренебрегали безопасностью для производительности. Самое интересное, что семейство AS400 позволило операционной системе создавать безопасные указатели, которые можно было бы дать непривилегированному коду, но которые непривилегированный код не мог подделать или подделать. Опять же, безопасность и, в то время как совместимый со стандартами, много неаккуратного, не отвечающего стандартам кода C / C ++ не будут работать в такой защищенной системе. Опять же, есть официальные стандарты,
Теперь я избавлюсь от своего мыла безопасности и упомяну о некоторых других способах, которыми указатели (различных типов) часто не адресованы в действительности: указатели на элементы данных, указатели на методы функций-членов и их статические версии больше, чем обычный адрес. Как говорится в этом посте :
Как вы, вероятно, догадываетесь из моих понтификаций по (в) безопасности, я принимал участие в аппаратных / программных проектах на C / C ++, где указатель рассматривался скорее как возможность, чем необработанный адрес.
Я мог бы продолжить, но я надеюсь, что вы поняли идею.
КРАТКОЕ РЕЗЮМЕ (которое я также поставлю вверху):
(0) представление об указателях как об адресах часто является хорошим средством обучения и часто является реальной реализацией указателей на обычные типы данных.
(1) Но на многих, возможно, большинстве, указатели компиляторов на функции не являются адресами, но больше, чем адрес (обычно в 2 раза, иногда больше), или на самом деле являются указателями на структуру в памяти, которая содержит адреса функций и тому подобное постоянный пул.
(2) Указатели на элементы данных и указатели на методы часто даже более странны.
(3) Устаревший код x86 с проблемами указателей FAR и NEAR
(4) Несколько примеров, особенно IBM AS / 400, с безопасными «жирными указателями».
Я уверен, что вы можете найти больше.
источник
LinearAddress = SegmentRegister.Selector * 16 + Offset
(обратите внимание, раз 16, а не сдвиг на 16). В защищенном режимеLinearAddress = SegmentRegister.base + offset
(без умножения любого вида; база сегмента сохраняется в GDT / LDT и кэшируется в регистре сегмента как есть ).Указатель - это просто еще одна переменная, которая используется для хранения адреса ячейки памяти (обычно это адрес памяти другой переменной).
источник
Вы можете видеть это таким образом. Указатель - это значение, представляющее адрес в адресуемой области памяти.
источник
Указатель - это просто еще одна переменная, которая может содержать адрес памяти, обычно другой переменной. Указатель, являющийся переменной, тоже имеет адрес памяти.
источник
Указатель переменного тока очень похож на адрес памяти, но с удаленными машинно-зависимыми деталями, а также некоторыми функциями, которых нет в наборе команд более низкого уровня.
Например, указатель C относительно богато типизирован. Если вы увеличиваете указатель через массив структур, он приятно переходит из одной структуры в другую.
Указатели подчиняются правилам преобразования и обеспечивают проверку типов во время компиляции.
Существует специальное значение «нулевого указателя», которое переносимо на уровне исходного кода, но представление которого может отличаться. Если вы назначаете целочисленную константу, значение которой равно нулю, указателю, этот указатель принимает значение нулевого указателя. То же самое, если вы инициализируете указатель таким образом.
Указатель может использоваться в качестве логической переменной: он проверяет истину, если она отлична от нуля, и ложь, если она равна нулю.
В машинном языке, если нулевой указатель представляет собой забавный адрес, такой как 0xFFFFFFFF, вам, возможно, придется иметь явные тесты для этого значения. С скрывает это от вас. Даже если нулевой указатель равен 0xFFFFFFFF, вы можете проверить его, используя
if (ptr != 0) { /* not null! */}
.Использование указателей, которые подрывают систему типов, приводит к неопределенному поведению, тогда как подобный код на машинном языке может быть хорошо определен. Ассемблеры будут собирать написанные вами инструкции, но компиляторы C будут оптимизировать, исходя из предположения, что вы не сделали ничего плохого. Если
float *p
указатель указывает наlong n
переменную и*p = 0.0
выполняется, компилятору не требуется обрабатывать это. Последующее использованиеn
не обязательно будет считывать битовую комбинацию значения с плавающей запятой, но, возможно, это будет оптимизированный доступ, основанный на предположении о "строгом псевдониме",n
которое не было затронуто! То есть предположение, что программа хорошо себя ведет, и поэтомуp
не должно указывать наn
.В C указатели на код и указатели на данные различны, но на многих архитектурах адреса совпадают. Могут быть разработаны компиляторы C, которые имеют «жирные» указатели, хотя целевая архитектура этого не делает. Жирные указатели означают, что указатели являются не просто машинными адресами, но содержат другую информацию, такую как информация о размере объекта, на который указывает объект, для проверки границ. Портативно написанные программы легко портировать на такие компиляторы.
Итак, вы можете видеть, что есть много семантических различий между машинными адресами и C-указателями.
источник
ptr != 0
не является нулевым тестом, пожалуйста, покажите его личность (но прежде чем сделать это, отправьте отчет об ошибке поставщику).Прежде чем понимать указатели, мы должны понимать объекты. Объекты - это сущности, которые существуют и имеют спецификатор местоположения, называемый адресом. Указатель - это просто переменная, как и любые другие переменные
C
с типом,pointer
чье содержимое интерпретируется как адрес объекта, который поддерживает следующую операцию.Указатель классифицируется в зависимости от типа объекта, на который он ссылается в данный момент. Единственная часть информации, которая имеет значение, - это размер объекта.
Любой объект поддерживает операцию
&
(адрес), которая извлекает спецификатор местоположения (адрес) объекта как указатель типа объекта. Это должно уменьшить путаницу, связанную с номенклатурой, так как это имеет смысл называть&
операцией объекта, а не указателем, результирующий тип которого является указателем типа объекта.Примечание. В этом объяснении я опускаю понятие памяти.
источник
&
«Address of», так как он больше привязан к объекту, чем к указателю как таковому »Адрес используется для идентификации части памяти фиксированного размера, обычно для каждого байта, как целое число. Это точно называется байтовым адресом , который также используется ISO C. Могут быть некоторые другие методы для создания адреса, например, для каждого бита. Однако, так часто используется только байтовый адрес, мы обычно опускаем «байт».
Технически, адрес никогда не является значением в C, потому что определение термина «значение» в (ISO) C:
(Подчеркнуто мной.) Однако в C. нет такого «типа адреса».
Указатель не тот же. Указатель является своего рода типом в языке Си. Существует несколько различных типов указателей. Они не обязательно подчиняются одинаковому набору правил языка, например, влиянию
++
значения типаint*
противchar*
.Значение в C может иметь тип указателя. Это называется значением указателя . Чтобы было ясно, значение указателя не является указателем на языке Си. Но мы привыкли смешивать их вместе, потому что в C это вряд ли будет неоднозначным: если мы называем выражение
p
как «указатель», это просто значение указателя, но не тип, так как именованный тип в C не является выражается выражением , но именем типа или именем определения типа .Некоторые другие вещи тонки. Как пользователь C, во-первых, нужно знать, что
object
означает:Объект - это объект, представляющий значения определенного типа. Указатель является типом объекта . Таким образом, если мы объявляем
int* p;
, тоp
означает «объект типа указателя» или «объект указателя».Обратите внимание, что в стандарте отсутствует «переменная», нормативно определенная стандартом (фактически, в нормативном тексте ISO C никогда не используется как существительное). Однако, неофициально, мы называем объект переменной, как это делает другой язык. (Но все же не совсем так, например, в C ++ переменная может иметь нормативный тип ссылки , который не является объектом.) Фразы «объект-указатель» или «переменная-указатель» иногда обрабатываются как «значение указателя», как указано выше, с возможная небольшая разница. (Еще один набор примеров - «массив».)
Так как указатель является типом, а адрес в C фактически «не имеет типа», значение указателя примерно «содержит» адрес. А выражение типа указателя может дать адрес, например
ISO C11 6.5.2.3
Обратите внимание, что эта формулировка введена WG14 / N1256, то есть ISO C99: TC3. В с99 есть
Он отражает мнение комитета: адрес не является значением указателя, возвращаемого унарным
&
оператором.Несмотря на вышеприведенную формулировку, в стандартах все еще есть некоторые проблемы.
ISO C11 6.6
ISO C ++ 11 5.19
(В недавнем черновом варианте стандарта C ++ используется другая формулировка, поэтому этой проблемы не существует.)
На самом деле и «адресная константа» в C, и «адресная константа-выражение» в C ++ являются константным выражением типов указателей (или, по крайней мере, «подобных указателям» типов начиная с C ++ 11).
И встроенный унарный
&
оператор называется «address-of» в C и C ++; аналогично,std::addressof
вводится в C ++ 11.Эти названия могут привести к неправильному представлению. Результате выражение имеет тип указателя, так что они будут интерпретироваться как: результат содержит / дает адрес, а не является адресом.
источник
Он говорит: «потому что это сбивает с толку тех, кто не знает, о чем идет речь», а также, это правда: если вы узнаете, о чем речь, вы не будете смущены. Теоретически указатель представляет собой переменную, которая указывает на другую, практически содержит адрес, который является адресом переменной, на которую он указывает. Я не знаю, почему должен скрывать этот факт, это не ракетостроение. Если вы понимаете указатели, вы на шаг приблизитесь к пониманию работы компьютеров. Преуспевать!
источник
Если подумать, я думаю, что это вопрос семантики. Я не думаю, что автор прав, так как стандарт C ссылается на указатель как на удерживающий адрес объекта, на который есть ссылка, как уже упоминалось здесь другими. Однако адрес! = Адрес памяти. Адрес может быть действительно любым в соответствии со стандартом C, хотя в конечном итоге он приведет к адресу памяти, сам указатель может быть идентификатором, смещение + селектор (x86), действительно любым, если он может описать (после отображения) любую память адрес в адресном пространстве.
источник
int i=5
-> я равен 5, тогда указатель - это адрес да. Кроме того, у нуля есть адрес. Обычно неверный адрес записи (но не обязательно, см. Режим x86-real), но адрес тем не менее. На самом деле существует только 2 требования для нуля: гарантируется сравнение неравного с указателем на реальный объект, а любые два нулевых указателя будут сравниваться одинаково.p
указатель,p+1
не всегда адрес увеличивается на 1.it's guaranteed to compare unequal to a pointer to an actual object
. Что касается арифметики указателей, то я не вижу смысла, значение указателя по-прежнему является адресом, даже если операция «+» не обязательно добавит к нему один байт.Еще один способ отличия указателя на C или C ++ от простого адреса памяти из-за различных типов указателей, которые я не видел в других ответах (хотя, учитывая их общий размер, я мог его не заметить). Но это, наверное, самый важный, потому что даже опытные программисты на C / C ++ могут запутаться:
Компилятор может предположить, что указатели несовместимых типов не указывают на один и тот же адрес, даже если они явно это делают, что может привести к поведению, которое было бы невозможным при использовании простой модели указателя == адреса. Рассмотрим следующий код (при условии
sizeof(int) = 2*sizeof(short)
):Обратите внимание, что есть исключение для
char*
, поэтому манипулирование значениямиchar*
возможно (хотя и не очень переносимо).источник
Краткий обзор: адрес AC - это значение, обычно представляемое в виде адреса памяти на уровне компьютера, с определенным типом.
Безусловное слово «указатель» неоднозначно. C имеет объекты указателя (переменные), типы указателя, выражения указателя и значения указателя .
Очень часто слово «указатель» используется для обозначения «объект указателя», и это может привести к некоторой путанице - вот почему я пытаюсь использовать «указатель» как прилагательное, а не как существительное.
Стандарт C, по крайней мере в некоторых случаях, использует слово «указатель» для обозначения «значение указателя». Например, описание malloc говорит, что оно «возвращает либо нулевой указатель, либо указатель на выделенное пространство».
Так какой адрес в С? Это значение указателя, т. Е. Значение определенного типа указателя. (За исключением того, что значение нулевого указателя не обязательно упоминается как «адрес», так как это не адрес чего-либо).
Стандартное описание унарного
&
оператора говорит, что оно «дает адрес своего операнда». Вне стандарта C слово «адрес» обычно используется для обозначения (физического или виртуального) адреса памяти, обычно одного слова размером (независимо от того, «слово» в данной системе).«Адрес» AC обычно реализуется как машинный адрес - так же, как
int
значение C обычно реализуется как машинное слово. Но адрес C (значение указателя) - это больше, чем просто адрес машины. Это значение, обычно представляемое в виде машинного адреса, и это значение определенного типа .источник
Значение указателя является адресом. Переменная-указатель - это объект, который может хранить адрес. Это правда, потому что именно так стандарт определяет указатель. Важно сообщить об этом новичкам на C, поскольку новички на C часто не понимают разницы между указателем и тем, на что он указывает (то есть они не знают разницы между оболочкой и зданием). Понятие адреса (у каждого объекта есть адрес, и это то, что хранит указатель) очень важно, потому что оно улаживает это.
Тем не менее, стандарт говорит на определенном уровне абстракции. Те, о которых автор говорит о том, кто «знает, о каких адресах», но кто не знаком с C, обязательно должны были узнать об адресах на другом уровне абстракции - возможно, с помощью языка ассемблера программирования. Нет гарантии, что реализация C использует то же представление для адресов, что и опкоды ЦП (называемые в этом отрывке «адресом хранилища»), о которых эти люди уже знают.
Он продолжает говорить об «совершенно разумных манипуляциях с адресом». Что касается стандарта C, то в принципе не существует такого понятия, как «совершенно разумное манипулирование адресом». Дополнение определяется по указателям, и это в основном это. Конечно, вы можете преобразовать указатель в целое число, выполнить несколько побитовых или арифметических операций, а затем преобразовать его обратно. Это не гарантирует работоспособность по стандарту, поэтому, прежде чем писать этот код, вам лучше узнать, как ваша конкретная реализация C представляет указатели и выполняет это преобразование. Это , вероятно , использует адрес представительства вы ожидаете, но он это делает не то, что это ваша вина , потому что вы не читали руководство. Это не путаница, это неправильная процедура программирования ;-)
Короче говоря, C использует более абстрактную концепцию адреса, чем автор.
Понятие адреса автора, конечно, также не является словом самого низкого уровня по этому вопросу. Что касается карт виртуальной памяти и адресации физической оперативной памяти для нескольких микросхем, то число, которое вы указываете ЦП как «адрес магазина», к которому вы хотите получить доступ, в основном не имеет ничего общего с тем, где данные, которые вы хотите, фактически находятся на аппаратном уровне. Это все слои косвенности и представления, но автор выбрал один для привилегии. Если вы собираетесь это сделать, когда говорите о C, выберите уровень C для привилегий !
Лично я не думаю, что замечания автора являются настолько полезными, кроме как в контексте введения Си программистам на ассемблере. Тем, кто прибывает из языков более высокого уровня, определенно не полезно говорить, что значения указателя не являются адресами. Было бы намного лучше признать сложность, чем сказать, что ЦПУ обладает монополией на то, чтобы сказать, что такое адрес, и, таким образом, значения указателя С "не являются" адресами. Это адреса, но они могут быть написаны не на тех языках, которые он имеет в виду. Я думаю, было бы целесообразно различать две вещи в контексте C как «адрес» и «адрес магазина».
источник
Проще говоря, указатели фактически являются смещенной частью механизма сегментации, который преобразуется в линейный адрес после сегментации, а затем в физический адрес после подкачки. Физические адреса на самом деле адресованы вам, баран.
источник