#c #memory
#c #память
Вопрос:
Разве вычисление разницы в адресах переменных стека не было бы способом определить, растет ли стек вверх или вниз?
int a;
int b;
printf ("Difference: %dn", (amp;b - amp;a); // if > 0, stack grows downwards
Комментарии:
1. «Вниз» не обязательно имеет смысл, потому что это полностью зависит от перспективы. Я не думаю, что хотя этот порядок переменных гарантирован, так что нет, я не думаю, что это сработает. Кажется , что это следует порядку объявления, когда типы данных имеют одинаковый размер, но я не думаю, что это обязательно должно быть так
2. примеры, которые я видел, либо используют рекурсивные функции, либо передают локальную переменную в функцию и сравнивают различия
3. @Carcigenicate не могли бы вы подробнее остановиться на части упорядочения переменных?
int a
выполняется первым иint b
после, так что разве они не должны быть размещены соответствующим образом?4. Только потому, что переменная объявлена раньше другой, она не обязательно помещается в том же порядке в памяти. Если компилятор считает, что он может лучше выровнять стек с другой конфигурацией, он может сохранить их в любом порядке, который он хочет. По моему опыту, когда данные имеют одинаковый размер, это, кажется, поддерживает порядок, поскольку любое из них привело бы к одинаковому выравниванию, но я не думаю, что это требуется.
5. @Carcigenicate so
b
может быть помещен вышеa
или наоборот в память стека и является неопределенным?
Ответ №1:
Помимо специфичных для реализации аспектов, сравнение адресов локальных переменных в одной и той же функции не является хорошим способом определить, увеличивается стек или уменьшается. Это потому, что реализация свободно упорядочивает переменные в памяти любым способом, который она считает нужным.
Переменные в стековом фрейме конкретной функции обычно будут вместе, однако, поэтому вы могли бы сделать это, сравнив адрес переменной в одной функции с адресом переменной в вызываемой функции. Например:
void check_stack(void *ptr)
{
int var;
if (ptr) {
if ((uintptr_t)amp;var > (uintptr_t)ptr) {
printf("stack is growing upn");
} else {
printf("stack is growing downn");
}
} else {
check_stack(amp;var);
}
}
И вызовите функцию как check_stack(NULL);
Также обратите внимание, что указатели сначала преобразуются в uintptr_t
. Это потому, что использование реляционных операторов над несвязанными указателями вызывает неопределенное поведение. A uintptr_t
— это целочисленный тип, который гарантированно способен содержать преобразованное значение указателя, и именно эти значения сравниваются.
Комментарии:
1. разве
var
amp;ptr
не одно и то же после первого рекурсивного вызова?2. В рекурсивном вызове
ptr
будет адресvar
из предыдущего вызова. Каждый вызов функции имеет свой собственныйvar
.3. таким образом, мы гарантируем, что порядок остается неизменным, поскольку мы сравниваем переменные, расположенные в разных стеках, в отличие от сравнения локальных переменных в одной и той же функции?
4. @xyf Мы сравниваем переменные в разных фреймах стека. Это тот же стек, просто другая его часть.
5. @xyf Один и тот же стек, разные кадры стека. Поскольку стеки по замыслу могут добавляться только к началу, вы знаете, что при вызове функции ее фрейм стека будет размещен выше фрейма вызывающей функции.