Реальная вещь о "->" и "." - PullRequest
16 голосов
/ 26 мая 2010

Я всегда хотел знать, в чем реальная разница между тем, как компилятор видит указатель на структуру (предположим в C) и на саму структуру.что компилятор делает: "значение pp + смещение атрибута" age "в структуре".

Но что он делает с person.p?Это было бы почти то же самое.Для меня «программист», p это не адрес памяти, это как «сама структура», но, конечно, это не то, как с этим справляется компилятор.

Полагаю, это скорее синтаксическая вещь, а компилятор всегда делает (&p)->age.

Я прав?

Ответы [ 6 ]

28 голосов
/ 26 мая 2010

p->q по сути является синтаксическим сахаром для (*p).q в том смысле, что он разыменовывает указатель p и затем переходит к соответствующему полю q внутри него. Это экономит печатать для очень распространенного случая (указатели на структуры).

По сути, -> делает две задержки (разыменование указателя, разыменование поля), в то время как . делает только одну (разыменование поля).

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

5 голосов
/ 26 мая 2010

Обновлено (см. Комментарии):

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

Напротив, pp->age должен быть скомпилирован как «значение pp + смещение поля age», так как значение pp может изменяться во время выполнения.

3 голосов
/ 26 мая 2010

Два оператора не эквивалентны даже с точки зрения компилятора. Оператор p.age преобразуется в адрес p + смещение age, а pp->age преобразуется в адрес , содержащийся в pp + смещение age.

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

Скажем, смещение возраста равно 5. Если p является структурой, ее адрес может быть 100, поэтому p.age ссылается на адрес 105.

Но если pp является указателем на структуру, ее адрес может быть 100, но значение, хранящееся по адресу 100, не является началом person структуры, это указатель. Таким образом, значение по адресу 100 (адрес , содержащийся в pp) может быть, например, 250. В этом случае pp->age ссылается на адрес 255, а не на 105.

1 голос
/ 04 июня 2010

Это вопрос, который я всегда задавал себе.

v.x, оператор member , действителен only для структур. v->x, член оператора указателя , действителен только для указателей структуры.

Так зачем иметь два разных оператора, поскольку нужен только один? Например, можно использовать только оператор .; компилятор всегда знает тип v, поэтому он знает, что делать: v.x, если v - это структура, (*v).x, если v - это указатель на структуру.

У меня есть три теории:

  • временная близорукость от K & R (какую теорию я бы хотел ошибочить)
  • упрощение работы для компилятора (практическая теория, учитывая время концепции C:)
  • удобочитаемость (какую теорию я предпочитаю)

К сожалению, я не знаю, какой из них (если есть) является правдой.

1 голос
/ 26 мая 2010

Поскольку p является локальной (автоматической) переменной, она сохраняется в стеке. Поэтому компилятор обращается к нему с точки зрения смещения относительно указателя стека (SP) или указателя кадра (FP или BP, в архитектурах, где он существует). Напротив, * p относится к адресу памяти [обычно], выделенному в куче, поэтому регистры стека не используются.

0 голосов
/ 26 мая 2010

В обоих случаях структура и ее члены рассматриваются как

адрес (человек) + смещение (возраст)

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

Краткий ответ: когда компилятор не оптимизирует, вы правы. Как только компилятор начинает оптимизацию, гарантируется только то, что указано в стандарте c .

Редактировать: Удалено некорректное расположение стека / кучи для "pp->", поскольку указанная структура может находиться как в куче, так и в стеке.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...