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

в C, предположим, у вас есть переменная variable_name. Предположим, он расположен по адресу 0xaaaaaaaa и по этому адресу памяти, у вас есть целое число 123. Другими словами, variable_name содержится 123.

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

5 ответов


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

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

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

toolchain (компилятор и компоновщик) работают вместе, чтобы назначить местоположение памяти для переменной. Задача компилятора-отслеживать все ссылки,и компоновщик позже вставляет правильные адреса.

строка "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    x00000004,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 строки больше не существует. Это были удобства для вы программист, но компьютер, конечно, не заботятся о них во время выполнения.


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


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

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


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

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

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


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

да.

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

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