Когда мы объявляем вектор stati c в C, сохраняется ли указатель на этот вектор? - PullRequest
1 голос
/ 21 марта 2020

Я хотел бы знать, если

int v [10];

выделяет (кроме 10 * sizeof (int) байтов) также указатель на целое число для этого вектора.

Ответы [ 3 ]

5 голосов
/ 21 марта 2020

Нет, дополнительного выделения указателя нет.

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

2 голосов
/ 21 марта 2020

Для понимания этого вопроса существует два концептуальных уровня.

Во-первых, 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.

1 голос
/ 21 марта 2020

Технически, когда вы объявляете массив v, вы смещаете стек памяти с диапазоном адресов, подходящим для хранения 10 ints. Это можно увидеть, если вы используете компилятор для генерации базового кода сборки ваших c инструкций. Сборка сохраняет адрес в этой позиции стека

Используя x86-64 g cc 9.3, я скомпилировал эту сборку с немного расширенной версией примера, который вы дали.

int main(void){
    int v [10];
    v[0] = 0;
    v[1] = 1;
    return 0;
}

Производит:

main:
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-48], 0
        mov     DWORD PTR [rbp-44], 1
        mov     eax, 0
        pop     rbp
        ret

Рассказывать вам все о том, что означает каждая инструкция, за пределами этого ответа, но вы можете видеть, как 0 и 1 выталкиваются в стек со смещением, соответствующим в размере Int. Причина, по которой компилятор не выделяет точно минимально необходимое пространство, связана с другими инструкциями по оптимизации, относящимися к реализациям компилятора.

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