В C ++ все ли имена по сути являются указателями «под капотом»?

0

В C++ имена фактически являются указателями под капотом? Например:

// num is a name that points to the address of the 
// memory location containing 32.53
double num (32.53);

*num; // so can we deference it and get 32.53?

 num; // same as *num but the compiler automatically 
      // dereferences the name for us?

Разъяснение:

Этот вопрос был своего рода странной смесью "Что происходит на уровне машины?" а также о семантике языка C++ указателей. Таким образом, я понимаю, почему ответы да/нет. "Да", потому что вне семантики языка можно было бы назвать идентификатор как относящийся к местоположению в памяти; и "Нет", потому что это все еще не указатель C++, и неверно относиться к двойнику без указателя, как показано в коде.

Поэтому я думаю, что оба лагеря успешно ответили на мой вопрос. Его можно было бы переформулировать следующим образом: "Поскольку имена относятся к ячейкам памяти, почему их нельзя рассматривать как скрытые указатели?" Но такой вопрос может порождать нечеткие дебаты или просто не стоит отвечать. Я тщательно рассмотрю ответы и попытаюсь найти ту, которая (я чувствую) отвечает на вопрос лучше всего с обоих углов. Другими словами, тот, который не просто говорит "Это не фиктивный указатель C++!" или "конечно, имя как-то указывает на память".

  • 0
    Где этот ответ говорит, что примитивные типы удовлетворяют OutputIterator ?
  • 0
    Я изменил свой ответ, чтобы быть более общим.
Показать ещё 15 комментариев
Теги:
pointers

8 ответов

4
Лучший ответ

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

Хотя адресация используется для разрешения имен имен во время выполнения, имена переменных не являются указателями в C++. Это связано с тем, что имена переменных не являются значениями времени выполнения, которые представляют собой местоположения; они во время компиляции (и в случае внешних имен с рычажной, время компоновки ) символов, обозначающих места.

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

Существует значение указателя времени выполнения, связанное с именем переменной, и вы можете получить доступ к этому свойству с помощью адреса оператора:

double *pnum = #

но не так:

*num; // so can we deference it and get 32.53?

Ты это пробовал? Унарный оператор разыменования требует выражения типа указателя; он не будет работать с выражением типа double. (Он может работать с выражением типа класса, если он соответствующим образом перегружен, что-то еще.)

Хотя с помощью оператора & мы можем получить указатель на место хранения с именем num, само имя num не является этим указателем.

Когда мы оцениваем код типа num = 3, скорее всего, речь идет об адресе. А может и нет. Например, если num оптимизирован в регистр, тогда это просто загружает 3 в этот регистр. Даже если num имеет адрес памяти, этот адрес не является программистом, видимым в этой ситуации. Указатели являются программно-видимыми: программист создает их, вытесняет их, разыгрывает их, сохраняет в переменных, передает их в функции и т.д.

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

4

Если вы расширите определение указателя на "любое концептуальное устройство, которое ссылается на некоторую информацию в памяти", то да, абсолютно.

Однако никто этого не делает.

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

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

  • 1
    Это отличный ответ высокого уровня, прекрасно сочетающийся с намерением спрашивающего. Это также ясно и кратко отвечает на вопрос относительно этого намерения. Я выбрал другой ответ, потому что чувствовал, что он будет более актуальным и полезным для большинства пользователей SO (у которых нет таких странных и нечетких вопросов, как у меня), потому что вместо устранения неоднозначности вопроса он отвечает подробно. разница между указателями C ++ и «концептуальной вещью, которая связывает имена с местоположениями данных». Я надеюсь, что мой критерий наилучшего ответа определяет ценность сайта.
  • 0
    @jakeliquorblues: Да, ответ Каз очень хороший. Снова большие пальцы руки :) (Я также наслаждаюсь вашим набегом от третьего лица!)
1

Нет.

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

Вы можете получить значение указателя (не объект указателя), взяв адрес объекта, как в &num, но вы можете это сделать для любого объекта.

Что касается *num, это незаконно, если num имеет тип double.

Имя объекта - это то, что относится к самому объекту; в этом смысле, если я сильно прищурился, я могу думать об этом как о каком-то "указателе". Но указатель в смысле C++ - это значение или объект, содержащий адрес памяти, и он существует во время выполнения программы; идентификатор существует только в исходном коде C++. Компилятор C++ будет иметь некоторую внутреннюю структуру данных времени компиляции, которая ссылается на объявленную переменную, которая будет включать (или ссылаться) информацию о ее имени и типе, но IMHO не разумно назвать эту структуру данных "указателем",, Вероятно, это будет нечто гораздо более сложное, чем адрес памяти. И имя переменной, объявленной внутри функции, будет ссылаться на разные объекты или вообще не на всех в разное время во время выполнения программы.

  • 0
    Вопрос не в том, создает ли num указатель, а в том, является ли он указателем «под капотом». Таким образом, ответ, да, в модели C ++, имя в значительной степени является способом ссылки на байты в памяти (в модели вычисления C ++). Доступ к объекту, если он не оптимизирован, работает путем загрузки или сохранения с использованием адреса объекта.
  • 1
    @Eric: На самом деле ответ «нет» в модели C ++. Только когда вы выйдете за рамки этой модели, ответ может стать нечетким «да».
Показать ещё 4 комментария
1

Нет.

Из определения типа указателя стандарта C (в §6.2.5/20):

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

(акцент мой).

В твоем случае:

double num (32.53);

у вас есть double, с num идентификатора, значение которого равно 32.53, которое не предназначено для использования в качестве значения указателя. Даже тип num - тип указателя.

Поэтому no, num не является указателем и, как сказал бы вам компилятор, если вы пытались скомпилировать:

*num;

вы не можете разыгрывать его.

0

Нет. Значение num, вероятно, хранится в регистре для такого простого примера. А на сегодняшних процессорах регистры не имеют адреса.

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

Таким образом, имя намного мощнее, чем указатель: оно следует за значением, когда оно перемещается в памяти. Создание и сохранение указателя &num запрещает такие перемещения, так как перемещение num приведет к недействительности указателя.

0

То, о чем вы говорите, действительно сводится к определению "lvalue".

Рассмотрим простую переменную, как вы дали в вопросе:

double num = 32.53;

num - это lvalue, что примерно соответствует тому факту, что оно относится к некоторому местоположению в памяти.

Когда он используется в выражении, значение lvalue может быть преобразовано в rvalue, которое переводит (грубо) на получение значения из этой ячейки памяти. Например, с учетом выражения типа:

num = num + 1.5;

num начинается как lvalue, но где он используется в правой части задания, он преобразуется в rvalue. Это в основном означает, что значение из этого местоположения в памяти выбрано, затем к нему добавляется 1,5, затем результирующее значение записывается обратно в место в памяти, на которое ссылается num.

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

Возможно, картина поможет:

Изображение 174551

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

Если мы определим что-то вроде:

double num2 = 12.34;
double *ptr = &num2;

Затем мы грубо видим ситуацию, изображенную во второй части картины. Мы определили num2 как double, точно так же, как num (но num2 другое значение). Затем мы определили указатель (pointer), который указывает на num2. Другими словами, когда мы разыскиваем pointer, получаем num2. Соединение с pointer на num2 является пунктирной линией, хотя это указывает на то, что если бы мы это выбрали, мы могли бы изменить это - мы могли бы присвоить адрес некоторого другого double pointer, который заставил бы pointer ссылаться на это другое местоположение переменной.

Хотя вы еще не спросили об этом (пока), третья часть рисунка показывает, что мы получим, если определим что-то вроде:

double num3 = 98.76;
double &reference = num3;

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

Чтобы ответить на ваш другой вопрос, с таким определением, как вы дали (double num = 32.53;), нет, вы не можете разыменовать num. Выражение вроде *num = 10.2; просто не будет компилироваться. Поскольку имя переменной всегда ссылается на это местоположение переменной, попытка использовать * для обозначения своего местоположения просто не требуется, поддерживается или разрешена.

  • 0
    double * ptr = & num2; слишком маленький для редактирования
  • 0
    @ Шагги: ой - спасибо.
Показать ещё 1 комментарий
0

Я думаю, что переменные в сборе работают так, то есть, что имя на самом деле является удобочитаемой меткой для адреса. (В этой статье указывается так, по крайней мере: http://www.friedspace.com/assembly/memory.php). Однако в C/C++ имя переменной является разыменованным адресом. Чтобы получить адрес, вы должны использовать оператор &.

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

  • 0
    Указатели в C ++ являются значениями, которые не обязательно хранятся в переменных.
0

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

Фактически, компилятор будет поддерживать таблицу символов, которая является отображением от идентификатора к местоположению объекта (здесь местоположение обычно не является адресом памяти, но смещено от нижней части стека или от начала сегмента данных, но то снова C++ указатели не являются физическими адресами либо в системах виртуальной памяти). Обычно эта таблица символов выводится компилятором для использования во время отладки. Динамические языки фактически использовали бы таблицу символов во время исполнения, чтобы поддерживать позднюю привязку и eval(). Но C++ не использует идентификаторы во время выполнения.

  • 0
    Но идентификатор не существует во время выполнения.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню