Где хранятся адреса указателей в языке Си?
Я изучаю C и в настоящее время изучаю указатели. Я понимаю принцип хранения адреса байта в памяти как переменной, что позволяет получить байт из памяти и записать его в адрес памяти.
Однако я не понимаю, где хранится адрес указателя. Допустим, значение указателя (адрес байта в памяти) хранится где - то в памяти-как программа может знать, где хранится указатель? Разве для этого не нужен указатель? указатель, приводящий к бесконечным указателям для указателей для указателей... ?
Обновить
Фактический вопрос: "как компилятор присваивает адреса памяти переменным". И я нашел этот вопрос, который указывает на эту тему.
Спасибо всем, кто ответил.
5 ответов:
Это деталь реализации, но...
Не все адреса хранятся в памяти. Процессор также имеет регистры, которые могут использоваться для хранения адресов. Есть только несколько регистров, которые можно использовать таким образом, возможно, 16 или 32, по сравнению с миллиардами байт, которые вы можете хранить в памяти.
Переменные в регистрах
Некоторые переменные будут храниться в регистрах. Если вам нужно быстро сложить некоторые числа, например, компилятор может использовать, например:,
%eax(который является регистром на x86) для накопления результата. Если оптимизация включена, то переменные обычно существуют только в регистрах. Конечно, только несколько переменных могут быть в регистрах в любой момент времени, поэтому большинство переменных должны быть записаны в память в какой-то момент.Если переменная сохраняется в памяти из-за нехватки регистров, это называется "разлив". Компиляторы работают очень усердно, чтобы избежать утечки регистров.
int func() { int x = 3; return x; // x will probably just be stored in %eax, instead of memory }Переменные на стек
Обычно один регистр указывает на специальную область, называемую "стеком". Таким образом, указатель, используемый функцией, может храниться в стеке, и адрес этого указателя может быть вычислен путем выполнения арифметики указателя на указателе стека. Указатель стека не имеет адреса, потому что это регистр, а регистры не имеют адресов.Компилятор выбирает макет стека, предоставляя каждой функции "кадр стека", достаточно большой, чтобы вместить все функции этой функции. переменная. Если оптимизация отключена, переменные обычно получают свой собственный слот во фрейме стека. При включенной оптимизации слоты будут использоваться повторно, совместно или оптимизированы полностью.void func() { int x = 3; // address could be "stack pointer + 8" or something like that }Переменные с фиксированными адресами
Другой альтернативой является хранение данных в фиксированном месте, например, "адрес 100".// global variable... could be stored at a fixed location, such as address 100 int x = 3; int get_x() { return x; // returns the contents of address 100 }На самом деле это не редкость. Помните, что "адрес 100" не соответствует оперативной памяти, обязательно-это фактически виртуальный адрес ссылка на часть виртуального адресного пространства вашей программы . Виртуальная память позволяет нескольким программам использовать "адрес 100", и этот адрес будет соответствовать различным частям физической памяти в каждой запущенной программе.
Абсолютные адреса могут также использоваться в системах без виртуальной памяти или для программ, которые не используют виртуальную память: загрузчики, ядра операционных систем и программное обеспечение для встраиваемых систем могут использовать фиксированные адреса без виртуальной памяти. память.Абсолютный адрес задается компилятором путем размещения "дыры" в машинном коде, называемойперемещением .
Затем компоновщик выбирает адрес дляint get_x() { return x; // returns the contents of address ??? // Relocation: please put the address of "x" here }xи помещает его в машинный код дляget_x().Переменные относительно счетчика программы
Еще одна альтернатива заключается в хранении данных в месте относительно кода, который выполняется.
// global variable... could be stored at address 100 int x = 3; int get_x() { // this instruction might appear at address 75 return x; // returns the contents of this address + 25 }Общие библиотеки почти всегда используют этот метод, что позволяет загружать общую библиотеку по любому адресу, доступному в адресном пространстве программы. В отличие от программ, общие библиотеки не могут выбрать свой адрес, потому что другая общая библиотека может выбрать тот же самый адрес . Программы также могут использовать этот метод, и это называется "независимым от позиции исполняемым файлом". Программы будут независимы от позиции на системах, которым не хватает виртуальной памяти, или для обеспечения дополнительной безопасности на системах с виртуальной памятью, так как это делает ее труднее писать код оболочки.
Точно так же, как и с абсолютными адресами, компилятор вставит "дыру" в машинный код и попросит компоновщика заполнить ее.
int get_x() { return x; // return the contents of here + ??? // Relocation: put the relative address of x here }
Переменная, которая является указателем, по-прежнему является переменной и действует как любая другая переменная. Компилятор знает, где находится переменная и как получить доступ к ее значению. Просто это значение случайно оказывается адресом памяти, вот и все.
Указатель - это просто переменная. Единственное различие между этим и, например, длинной переменной состоит в том, что мы знаем, что то, что хранится в переменной указателя, является адресом памяти, а не целым числом.
Таким образом, адрес переменной-указателя можно найти таким же образом, как и адрес любой другой переменной. Если вы храните этот адрес в какой-то другой переменной, у этой тоже будет адрес, конечно.Ваша путаница, кажется, происходит от того, что указатель (то есть адрес переменной), в свою очередь, может быть сохранен. Но его не нужно хранить нигде (вы делаете это только тогда, когда вам по какой-то причине нужен этот адрес). С точки зрения вашей программы, любая переменная является более или менее именованной ячейкой памяти. Таким образом," указатель на переменную "- это именованная ячейка памяти, содержащая значение, которое должно" указывать "на другую ячейку памяти, отсюда и название"указатель".
Предположим, что значение указателя (адрес байта в памяти) хранится где-то в памятиАдрес байта, который вы выделили, скажем так
Компилятор ссылается наchar ch = 'a';В таблице символов с правым смещением. Во время выполнения инструкции, генерируемые компилятором, будут использовать это смещение для перемещения его из первичной памяти в регистр для какой-либо операции над ним.
Указатель в том смысле, о котором вы спрашиваете, не хранится в любом месте, это просто тип, когда вы ссылаетесь на адрес переменной, , если только вы явно не создадите указатель переменная для хранения его так
Таким образом, здесь нет понятия рекурсии.&ch; // address of ch not stored anywhere char *p = &ch; // now the address of ch is stored in p
С точки зрения компиляторов, объявляет ли u указатель или общую переменную-это просто пространство памяти.При объявлении переменной ей выделяется определенный блок памяти.
Переменная может быть любой либо общей переменной, либо указателем. Таким образом, в конечном счете у нас есть переменные (даже указатели являются только переменными), и они имеют место в памяти.
Comments