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

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

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

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

6 ответов


нет, не в целом.

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

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

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


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

#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, как ожидалось.

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


это не гарантируется никаким стандартом, но

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

#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 на машине я печатаю это.


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

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

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

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

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

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


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


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