Я всегда хотел знать то, что является реальной вещью различие того, как компилятор видит, указатель на структуру (в C предполагают), и сама структура.
struct person p;
struct person *pp;
pp->age
, Я всегда предполагаю, что компилятор делает: "значение стр + смещение атрибута "возраст" в структуре".
Но с чем это делает person.p
? Это было бы почти то же. Для меня "программист", p не является адресом памяти, как сама "структура", но конечно это не то, как соглашение о компиляторе с ним.
Мое предположение, это - больше синтаксической вещи, и компилятор всегда делает (&p)->age
.
Я корректен?
Обновлено (см. Комментарии):
Вы правильно поняли, но есть важное различие только для глобальных и статических переменных : когда компилятор видит p.age
для глобальной или статической переменной, он может заменить ее во время компиляции точным адресом поля age
в структуре.
Напротив, pp-> age
должно быть скомпилировано как «значение pp + смещение поля age
», поскольку значение pp может измениться во время выполнения.
Поскольку p является локальным (автоматически) переменная, она хранится в стеке. Поэтому компилятор обращается к нему с точки зрения смещения относительно указателя стека (SP) или указателя кадра (FP или BP в архитектурах, где он существует). Напротив, * p относится к адресу памяти, [обычно] выделенному в куче, поэтому регистры стека не используются.
В обоих случаях структура и ее члены адресуются по
address(person) + offset(age)
Использование p со структурой, хранящейся в стековой памяти, дает компилятору больше возможностей для оптимизации использования памяти. Он может хранить только возраст, а не всю структуру, если больше ничего не используется - это делает адресацию с помощью приведенной выше функции неудачной (я думаю, что чтение адреса структуры останавливает эту оптимизацию).
Структура в стеке может вообще не иметь адреса в памяти. Если структура достаточно мала и живет недолго, она может быть сопоставлена с некоторыми регистрами процессора (так же, как и приведенная выше оптимизация для чтения адреса).
Короткий ответ: когда компилятор не оптимизирует, вы правы. Как только компилятор начинает оптимизировать, гарантируется только то, что определено стандартом c.
Edit: Удалено ошибочное местоположение в стеке/куче для "pp->", так как указатель на struct может находиться как в куче, так и в стеке.
Эти два оператора не эквивалентны даже с «точки зрения компилятора». Оператор p.age
транслирует в адрес p
+ смещение age
, а pp-> age
переводит в адрес содержится в pp
+ смещение возраста
.
Адрес из переменной и адрес , содержащиеся в (указатель) переменной, - очень разные вещи.
Скажем, смещение возраста равно 5. Если p
- это структура, ее адрес может быть 100, поэтому p.age
ссылается на адрес 105.
Но если ] pp
- указатель на структуру, его адрес может быть 100, но значение, хранящееся по адресу 100, не является началом структуры person
, это указатель. Таким образом, значение по адресу 100 (адрес , содержащийся в pp
) может быть, например, 250. В этом случае pp-> age
ссылается на адрес 255, а не 105. .
p->q
- это, по сути, синтаксический сахар для (*p).q
, поскольку он разыменовывает указатель p
, а затем переходит к соответствующему полю q
внутри него. Это экономит ввод для очень распространенного случая (указатели на структуры).
По сути, ->
делает два обращения (разыменование указателя, разыменование поля), в то время как .
делает только одно (разыменование поля).
Из-за фактора множественных ссылок, ->
не могут быть полностью заменены компилятором на статический адрес и всегда будут включать, по крайней мере, вычисление адреса (указатели могут динамически изменяться во время выполнения, поэтому местоположения также будут изменяться), тогда как в некоторых случаях .
операции могут быть заменены компилятором доступом к фиксированному адресу (поскольку адрес базовой структуры также может быть фиксированным).
Это вопрос, который я всегда задавал себе.
v.x
, оператор member, действителен только для структур.
v->x
, оператор member of pointer, действителен только для указателей struct.
Так зачем же иметь два разных оператора, если нужен только один? Например, только
; компилятор всегда знает тип v
, поэтому он знает, что делать: v.x
, если v
- struct, (*v).x
, если v
- указатель struct.
У меня есть три теории:
К сожалению, я не знаю, какая из них (если вообще какая-нибудь) верна.