Для понимания этого вопроса существует два концептуальных уровня.
Во-первых, C определяется в терминах абстрактной машины. В этой модели v
идентифицирует массив из десяти int
. v
не указатель; «V» - это идентификатор, а идентифицируемая вещь, названная v
, представляет собой массив из десяти int
. Там нет указателя. Когда v
используется в исходном коде в выражении, отличном от взятия его размера (с sizeof
) или его адреса (с &
), оно автоматически преобразуется в указатель. Этот указатель, по сути, изготавливается по мере необходимости - он не был (в модели, описанной в стандарте C) где-либо сохранен или загружен из любого места. Это просто делается при необходимости.
Во-вторых, когда C реализации используют массивы, они имеют различные способы обращения к памяти. Если массив является объектом stati c (потому что он был определен вне какой-либо функции), он обычно помещается в какое-то место в сегменте памяти, зарезервированном для данных stati c. Существуют различные способы, которыми реализации C ссылаются на эту память, в том числе:
- Компилятор помещает в объектный файл информацию о том, на какие области памяти он хочет ссылаться. Когда программа связана и загружена, компоновщик и загрузчик корректируют эту информацию и при необходимости изменяют машинные инструкции, чтобы они ссылались на конечный адрес объекта. Это может привести к инструкциям, которые содержат полный абсолютный адрес объекта, или к инструкциям, которые ссылаются на объект по тому, как далеко он находится от некоторого регистра, который содержит базовый адрес.
- Когда программа загружена, загрузчик выбирает где поместить данные в память и устанавливает регистр для хранения базового адреса для данных. Когда компилятор компилирует программу, он пишет инструкции, которые ссылаются на объекты stati c, используя смещения, указывающие, как далеко объекты находятся от этого базового адреса.
Если объект автоматический c вместо stati c (как определено внутри функции без ключевого слова static
), типичные реализации C используют память в стеке. Этот метод аналогичен методу base-register-and-offset, описанному выше, но базовый регистр - это специальный регистр, называемый указателем стека, который настраивается при вызове каждой функции для указания на память, зарезервированную только для использования этой конкретной вызов функции.
Всякий раз, когда используется метод base-register-and-offset, абсолютный адрес никогда не существует таким образом, который наблюдается программой. Например, инструкция может содержать ссылку на местоположение, такую как 38(sp)
(как описано на языке ассемблера), что означает «38 байт за пределы, на которые указывает указатель стека». Процессор берет содержимое указателя стека, добавляет к ним 38 и извлекает (если читает) содержимое этого местоположения из памяти. В этом случае абсолютный адрес мгновенно существует где-то внутри процессора, где он добавляет 38.