#c #assembly
#c #сборка
Вопрос:
У меня есть следующий код
#include<stdio.h>
int adunare(int a,int b)
{
int c=3;
int d=6;
while(c>10) c ;
if(c>15) return a b c d;
else return a b c-d;
}
int main()
{
int w=5;
int y=6;
printf("%d",adunare(w,y));
}
Мой вопрос заключается в том, что в сборке он помещает переменную w, y в [esp 24] , [esp 28].
Почему он помещает туда мои переменные?
Я знаю, что локальные переменные всегда [ebp-….] .
Почему здесь это не [ebp-..]?
Комментарии:
1. для C не требуется даже наличие «стека», поэтому, если это так, детали зависят от конкретной реализации. Если существует пользовательский стек, который также является тем же стеком, что и аппаратный стек, пока локальные переменные не повреждены вызовами функций или прерываниями, они безопасны. Индексируются ли они отрицательным смещением от
ebp
или положительным смещением отesp
, это просто соглашение реализации.2. В крайнем случае переменная может быть полностью удалена (если ее использование не оказывает никакого влияния на другие вещи, например
for (size_t i = 0; i < 1e6; i) {}
, будет полностью удалено оптимизатором, поэтомуi
не существует нигде, кроме источника. Тем не менее двоичный файл является допустимым (по определению языка C ).
Ответ №1:
Я знаю, что локальные переменные всегда [ebp-….]
Это не так (о чем, я полагаю, свидетельствует и ваш вопрос).
Компилятору разрешено компилировать действительно наивно, всегда используя указатели фрейма (даже в функциях, которые не выполняют распределение стека с переменным размером) и всегда помещая локальные файлы в стек в первую очередь (что определенно не является правилом). На первом курсе университета иногда притворяются, что это нормально, чтобы все было просто.
Обычно возможно не использовать указатель фрейма, он работает в основном так же, как если бы вы его использовали, за исключением того, что смещения вычисляются относительно указателя стека, который теперь вам разрешено перемещать только предсказуемыми способами. Поскольку она должна быть предсказуемой (то есть каждая инструкция, которая ссылается на слот стека, может использовать для этого постоянное смещение), эта оптимизация не может использоваться в функциях, которые используют alloca
или VLA. В вашем примере функции ни один из них не используется, поэтому указатель на фрейм не требуется.
Также в целом вы не должны ожидать, что локальные переменные будут соответствовать определенным слотам стека в первую очередь, независимо от того, как они адресуются. Разрешено, обычно и часто полезно хранить переменную в регистре в течение всего срока службы переменной. Особенно, если это время жизни короткое или если плотность использования очень высока. Кроме того, переменные с неперекрывающимися временами жизни могут (и должны, поскольку это уменьшает размер стека) совместно использовать слоты стека, поскольку в любой момент времени может потребоваться не более одного из них (благодаря предположению о неперекрывающихся временах жизни).
Также допускается переход переменной из одного слота стека в другой, это может произойти, когда вы меняете местами две переменные таким образом, чтобы подкачка разрешалась «виртуально», просто изменяя, в каком слоте стека находятся переменные, и фактически не обмениваясь данными.
Комментарии:
1. @user6575913 конечно, если вы хотите это назвать
2. хорошо. спасибо за разъяснение. я только начал изучать это (курс безопасности), и я готов узнать больше.
Ответ №2:
Вероятно, это оптимизация компилятора. Переменные не используются в пределах main
области видимости, поэтому помещаются непосредственно в стек, готовые к вызову функции.
Комментарии:
1. но теоретически это так, как я уже сказал, нет?
2. @user6575913 — Я не уверен, но я считаю, что нет определения того, как должен компилироваться код, только то, как он должен «вести себя». Очевидно, что разные платформы имеют разные компиляции.