Подтвердить что ты не робот

Как имена переменных хранятся в памяти в C?

В C, скажем, у вас есть переменная с именем variable_name. Скажем, он расположен в 0xaaaaaaaa, и на этом адресе памяти у вас есть целое число 123. Таким образом, variable_name содержит 123.

Я ищу разъяснения вокруг фразы "variable_name находится в 0xaaaaaaaa". Как компилятор распознает, что строка "variable_name" связана с этим конкретным адресом памяти? Является ли строка "variable_name" хранимой где-то в памяти? Компилятор просто подставляет variable_name для 0xaaaaaaaa всякий раз, когда он его видит, и если да, не нужно ли ему использовать память для этой замены?

4b9b3361

Ответ 1

Переменные имена больше не существуют после запуска компилятора (запрет специальных случаев, таких как экспортированные глобальные переменные в разделяемых библиотеках или символы отладки). Весь акт компиляции предназначен для того, чтобы использовать эти символические имена и алгоритмы, представленные вашим исходным кодом, и превратить их в собственные машинные инструкции. Так что да, если у вас есть глобальный variable_name, а компилятор и компоновщик решают поставить его на 0xaaaaaaaa, тогда везде, где он используется в коде, он будет доступен только через этот адрес.

Итак, чтобы ответить на ваши буквальные вопросы:

Как компилятор распознает, что строка "имя_переменной" связана с этим конкретным адресом памяти?

Инструментальная цепочка (компилятор и компоновщик) работает вместе, чтобы назначить ячейку памяти для переменной. Это задание компилятора, чтобы отслеживать все ссылки, а компоновщик помещает в нужные адреса позже.

Сохранена ли строка "variable_name" где-то в памяти?

Только во время работы компилятора.

Является ли компилятор просто заменять variable_name на 0xaaaaaaaa всякий раз, когда он его видит, и если да, то не нужно ли использовать память для этой замены?

Да, это в значительной степени то, что происходит, за исключением двухступенчатой ​​работы с компоновщиком. И да, он использует память, но это память компилятора, а не что-то во время выполнения для вашей программы.

Пример может помочь вам понять. Попробуйте эту программу:

int x = 12;

int main(void)
{
    return x;
}

Довольно просто, правда? ОК. Возьмите эту программу и скомпилируйте ее и посмотрите на разборку:

$ cc -Wall -Werror -Wextra -O3    example.c   -o example
$ otool -tV example
example:
(__TEXT,__text) section
_main:
0000000100000f60    pushq   %rbp
0000000100000f61    movq    %rsp,%rbp
0000000100000f64    movl    0x00000096(%rip),%eax
0000000100000f6a    popq    %rbp
0000000100000f6b    ret

Посмотрите, что строка movl? Он захватывает глобальную переменную (в данном случае относительный путь указателя). Больше не упоминается x.

Теперь сделаем это немного сложнее и добавим локальную переменную:

int x = 12;

int main(void)
{  
    volatile int y = 4;
    return x + y;
}

Разборка для этой программы:

(__TEXT,__text) section
_main:
0000000100000f60    pushq   %rbp
0000000100000f61    movq    %rsp,%rbp
0000000100000f64    movl    $0x00000004,0xfc(%rbp)
0000000100000f6b    movl    0x0000008f(%rip),%eax
0000000100000f71    addl    0xfc(%rbp),%eax
0000000100000f74    popq    %rbp
0000000100000f75    ret

Теперь есть две инструкции movl и инструкция addl. Вы можете видеть, что первый movl инициализирует y, который, по его мнению, будет в стеке (базовый указатель - 4). Затем следующий movl получает глобальный x в регистр eax, а addl добавляет y к этому значению. Но, как вы можете видеть, строки буквально x и y больше не существуют. Они были для вас полезными, программист, но компьютер, конечно, не заботится о них во время выполнения.

Ответ 2

Компилятор C сначала создает таблицу символов, в которой сохраняется взаимосвязь между именем переменной и местом, где она находится в памяти. При компиляции он использует эту таблицу для замены всех экземпляров переменной на определенную ячейку памяти, как указывали другие. Вы можете найти намного больше на нем на странице Википедии.

Ответ 3

Все переменные заменяются компилятором. Сначала они заменяются ссылками, а позже компоновщик помещает адреса вместо ссылок.

Иными словами. Имена переменных больше не доступны, как только компилятор выполнил

Ответ 4

Это то, что называется деталью реализации. Хотя то, что вы описываете, имеет место во всех компиляторах, которые я когда-либо использовал, это не обязательно. Компилятор AC может помещать каждую переменную в хэш-таблицу и искать их во время выполнения (или что-то в этом роде), и на самом деле ранние интерпретаторы JavaScript сделали именно это (теперь они делают компиляцию Just-In-TIme, что приводит к чему-то более сырую).

В частности, для обычных компиляторов, таких как VС++, GCC и LLVM: компилятор обычно назначает переменную местоположению в памяти. Переменные глобальной или статической области получают фиксированный адрес, который не изменяется во время работы программы, а переменные внутри функции получают адрес стека, то есть адрес относительно текущего указателя стека, который изменяется каждый раз, когда функция называется. (Это упрощение.) Адреса стеков становятся недействительными, как только функция возвращается, но имеют преимущество использования нулевого накладного расхода для использования.

Как только переменная имеет назначенный ей адрес, больше нет необходимости в имени переменной, поэтому она будет отброшена. В зависимости от типа имени имя может быть отброшено во время препроцесса (для имен макросов), времени компиляции (для статических и локальных переменных/функций) и времени ссылки (для глобальных переменных/функций). Если символ экспортируется ( сделанные видимыми для других программ, чтобы они могли получить к нему доступ), имя обычно останется где-то в "таблице символов", которая занимает тривиальное количество памяти и дискового пространства.

Ответ 5

Компилятор просто заменит имя_переменной для 0xaaaaaaaa когда он видит это,

Да.

не нужно ли использовать память для этой замены?

Нет, потому что компилятор делает это для вас. Программа, созданная компилятором, ничего не знает об именах ваших переменных.

Ответ 6

Компилятор просто подставляет переменную_имя для 0xaaaaaaaa всякий раз, когда видит ее

Да.

и если да, не нужно ли использовать память для выполнения этой замены?

Да. Но это компилятор, после того как он скомпилировал ваш код, почему вы заботитесь о памяти?