2014-11-12 3 views
5

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

int k = 5; 
int j = 3; 
short int i; 

if (k > j) i = 1; 

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

Для краткого объявления int i я предполагаю, что в стек будет выделено 2 байта. Для инт к и Int J для обоих 4 байта получают наделенный значения 5 и 3. Таким образом, стек будет выглядеть следующим образом

---------- <- stack pointer 
int i 
---------- 
int k = 5 
---------- 
int j = 3 
---------- 

поэтому для если заявление вы должны совать Int я, чтобы добраться до условия k и j, и если да, то где int i go? Все это кажется очень трудоемким и утомительным, если это так, что C выполняет локальные переменные.

Так это на самом деле, как C это или я все это делаю?

+0

на самом деле ваше расположение элементов в стеке неверно, так как я короткий, а не int. и стек растет вниз в памяти, а не вверх, а локальные переменные помещаются в стек в обратном порядке. Независимо от того, какое значение в локальном стеке не выбрано, скорее используется смещение от указателя стека, поэтому, ссылаясь на «k», получается «прочитанное слово (некоторый регистр)» из sp [(offset to k) »аналогично для j '. и 'i' задается как «написать halfword sp [offset to i] из (нижняя половина регистра, содержащего результат k + j), в общем случае вы можете думать о стеке как длинном массиве с разделами – user3629249

ответ

4

Вы когда-либо так слегка взломали его.

Да, локальные (auto) переменные обычно хранятся в стеке. Однако при чтении они не выталкиваются из стека; на них ссылается смещение от указателя стека.

Возьмем следующий код:

x = y + z; 

, где каждый из x, y и z выделяются в стеке.Когда компилятор генерирует эквивалентный машинный код, он будет относиться к каждой переменной, смещение от заданного регистра, вроде как:

mov -8(%ebp), %eax 
add -12(%ebp), %eax 
mov %eax, -4(%ebp) 

На x86 архитектур, %ebp является указателемкадра; стек разбивается на кадры, где каждый кадр содержит параметры функции (если есть), адрес возврата (то есть адрес команды, следующий за вызовом функции), и локальные переменные (если они есть). В системах, с которыми я знаком, стек растет «вниз» к 0, а локальные переменные хранятся «ниже» указателя кадра (нижние адреса), следовательно, отрицательное смещение. В приведенном выше коде предполагается, что x находится на уровне -4(%ebp), y составляет -8(%ebp), а z - по номеру -12(%ebp).

Все будет удалено из стека , когда функция вернется, но не раньше.

EDIT

Пожалуйста, обратите внимание, что ни один этого уполномочен определением языка C. Язык не требует использования стека времени выполнения на всех (хотя компилятор будет сукой для реализации без одного). Он просто определяет время жизни переменных auto как от конца их объявления до конца их охватывающей области. Стек делает это легко, но это не требуется.


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

7

Стек не является стеком. Это все равно случайный доступ память, то есть вы можете получить доступ к любому местоположению в постоянное время. Единственная цель дисциплины стека - дать каждой функции вызов своей собственной, частной области памяти, что функция может быть уверена, что ее никто не использует.

+0

Действительно, если вы посмотрите при разборке функции C вы не найдете push и pop для локальных переменных, вы найдете постоянное смещение от указателя базового стека - переменные находятся в очень предсказуемом месте, в дополнение к тому, чтобы быть в оперативной памяти. Например, инструкция по загрузке 'k' в примере OP будет выглядеть так:' mov eax, [ebp-4] '- сам указатель стека, будучи переменным внутри функции, даже не используется вообще для локальных пользователей. –

+0

Кажется, что несколько источников не согласны с вами, вот один из них: http://gribblelab.org/CBootcamp/7_Memory_Stack_vs_Heap.html примечание: «Он представляет собой структуру данных« FILO »(первая в прошлом), то есть управляемый и оптимизированный процессором ». Я должен указать, что я не говорю, что он использует указатель аппаратного стека, но он инфинирует стек FILO. – Anthony

+1

Возможно, это поможет просмотреть сгенерированный запуск кода 'gcc -c foo.c', затем' objdump -d foo.o '(необязательно добавляя' -M intel' в objdump, если вы похожи на меня и ненавидите синтаксис AT & T), и вы можете увидеть, что на самом деле сгенерировано. Хотя я думаю, что это не поможет, если вы не можете прочитать язык ассемблера ... но, тем не менее, команда вызова вызывает адрес возврата в стек, тогда функция использует память рядом с ней для локальных пользователей, затем возвращая снова всплывающие подсказки. Поэтому он похож на стек массивов, а не на чистый стек. –

0

В верхней части стека, на процессоре Intel и многих других, ссылается адрес, хранящийся в регистре cpu, вызывает его SP, который копируется на базовый указатель, называет его BP; многие машинные инструкции позволяют вызывать адресное выражение, состоящее из текущего BP в сочетании с смещением байта. поэтому в вашем примере i будет смещено 0, j будет смещено -2, а k будет смещено -6.

Если бы просто было разрешено сравнение содержимого адресов -6 (BP) и -4 (BP). фактические значения смещения могут отличаться от реализации к реализации; но, это общая идея ...

4

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

Смежные вопросы