Подтвердить что ты не робот

Как найти, выделяется ли переменная в стек или кучу?

Наткнулся на этот вопрос интервью где-то,

В C, Учитывая переменную x, как вы узнаете, выделено ли пространство для этой переменной в стек или кучу?

(Есть ли способ узнать это программно и не нужно проходить через таблицу символов и т.д. И обнаруживает ли какое-либо пространство в стеке или куче какие-либо практические последствия?)

4b9b3361

Ответ 1

Нет, не в общем.

Знаете ли вы о gcc -fsplit-stack?

Реализация должна решить, следует ли выделять непрерывный стек или стек, где блоки чередуются с блоками кучи в памяти. Удача выяснить, был ли выделен блок для кучи или стека, когда последний разбит.

Примечание: это бесполезная информация в любом случае...

Ответ 2

Если вы работаете над архитектурой, которая хранит стек на большем адресе, чем куча, вы можете сравнить адрес переменной со дном стека. Используя API-интерфейс pthread threading, это сравнение будет выглядеть так:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <inttypes.h>

int is_stack(void *ptr)
{
  pthread_t self = pthread_self();
  pthread_attr_t attr;
  void *stack;
  size_t stacksize;
  pthread_getattr_np(self, &attr);
  pthread_attr_getstack(&attr, &stack, &stacksize);
  return ((uintptr_t) ptr >= (uintptr_t) stack
          && (uintptr_t) ptr < (uintptr_t) stack + stacksize);
}

Тест:

int main()
{
  int x;
  int *p1 = malloc(sizeof(int));
  int *p2 = &x;

  printf("%d %d\n", is_stack(p1), is_stack(p2));
  return 0;
}

... печатает 0 1, как ожидалось.

Приведенный выше код не будет обнаруживать хранение из стеков в других потоках. Для этого код должен отслеживать все созданные потоки.

Ответ 3

Это НЕ гарантируется никаким стандартом BUT

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

#include <iostream>
#include <stdlib.h>
int main()
{
int x = 0;
int* y = new int;

unsigned int a1 = (int) &x;
unsigned int a2 = (int) y;

std::cout<<std::hex<<a1<<"  "<<a2<<std::endl;
}

дает вывод ffbff474 21600 на машине, которую я набираю.

Ответ 4

Это может быть трюк. Переменные имеют либо автоматическую, либо статическую продолжительность хранения [*]. Вы можете с уверенностью сказать, что автоматика выделяется "в стеке", по крайней мере предполагая, что они не оптимизированы в регистры. Это не требование стандарта, что существует "стек", но соответствующая реализация C должна поддерживать стек вызовов и связывать автоматические переменные с уровнями стека вызовов. Так что каковы бы ни были детали того, что он на самом деле делает, вы можете в значительной степени назвать это "стек".

Переменные со статической продолжительностью хранения обычно содержат один или несколько разделов данных. Из POV ОС разделы данных могут быть выделены из кучи до запуска программы, но из POV программы они не имеют никакого отношения к "свободному хранилищу".

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

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

Объекты также могут иметь динамическую продолжительность хранения (что обычно означает, что "выделено в куче" означает означать), но такие объекты не являются переменными, поэтому это был бы трюк, если он есть.

[*] Или thread-local в C11 и С++ 11.

Ответ 5

Я не думаю, что у него есть решения. Код может корректировать адрес var по стеку (куче), но это не будет точным способом. В лучшем случае код может работать только на некоторых определенных платформах.

Ответ 6

Невозможно определить, что по местоположению памяти компилятор должен был бы поддерживать его с помощью isstack(), чтобы быть переносимым.