Хранится ли стек времени выполнения в сегменте данных памяти?

StackOverflow https://stackoverflow.com/questions/802656

Вопрос

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

У меня есть фундаментальная программа - имеет 2 функции, первая из которых foo, а другая main (точка входа).

void foo(){
    // do something here or dont
}

int main(){

    int i = 0;

    printf("%p %p %p\n",foo, &i, main);

    system("PAUSE");
    return EXIT_SUCCESS;
};

Выходные данные программы показаны ниже, локальная переменная main i расположена совершенно в другом месте.integer - это тип значения, но я проверил его еще раз с помощью локального указателя char * на main и получил аналогичные результаты.

00401390 0022FF44 00401396
Press any key to continue . . .

В основном я понимаю, что код и переменные распределены по разным сегментам памяти (сегмент кода / сегмент данных).Итак, в принципе, правильно ли говорить, что стек вызовов сворачивает основную информацию о выполнении функций (их локальные переменные, параметры, точки возврата) и сохраняет их в сегменте данных?

Это было полезно?

Решение

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

Но основной ответ, как сказали @Richie и @Paul, - "да". Когда ваш компилятор и компоновщик заканчивают работу с кодом, он разбивается на то, что в UNIX известно как сегменты "текст" и "данные".A текстовый сегмент содержит инструкции и некоторые виды статических данных;сегмент данных содержит, ну, в общем, данные.

Затем большая часть сегмента данных выделяется для стека и кучи.Другие блоки могут быть выделены для таких вещей, как статические или внешние структуры данных.

Так что да, когда программа запускается, программный счетчик деловито извлекает инструкции из сегмента, отличного от данных.Теперь мы перейдем к некоторым архитектурным зависимостям, но в целом, если у вас сегментированная память, ваши инструкции построены таким образом, чтобы извлечение байта из сегментов было максимально эффективным, В старой архитектуре 360 они имели базовые регистры, в x86 есть куча проблем, которые выросли по мере того, как адресное пространство перешло от старых 8080-х к современным процессорам, но все инструкции очень тщательно оптимизированы, потому что, как вы можете себе представить, инструкции выборки и их операнды используются очень интенсивно.

Теперь мы переходим к более современным архитектурам с виртуальной памятью и блок управления памятьюs.Теперь у машины есть специальное оборудование, которое позволяет программе обрабатывать адресное пространство как большой плоский диапазон адресов;различные сегменты просто помещаются в это разрядное виртуальное адресное пространство.Задача MMU состоит в том, чтобы взять виртуальный адрес и преобразовать его в физический адрес, включая то, что делать, если этого виртуального адреса в данный момент вообще нет в физической памяти.Опять же, аппаратное обеспечение MMU очень сильно оптимизировано, но это не значит, что есть НЕТ связанные с производительностью затраты.Но по мере того, как процессоры становились быстрее, а программы - крупнее, это становилось все менее и менее важным.

Другие советы

Да, это совершенно верно.Код и данные хранятся в разных частях памяти с разными разрешениями.Стек содержит параметры, адреса возврата и локальные ("автоматические") переменные и работает с этими данными.

ДА.

Представьте, что ваша память кода - это ROM, а ваша память данных - RAM (распространенная архитектура небольших микросхем).Затем вы видите стопку должен находиться в памяти данных.

Что ж, я могу говорить за СПАРКА:

ДА.Когда вы запускаете программу, она считывается дважды (по крайней мере, в SPARC).Программа загружается в память, и после этого загружаются все выделения массива / стека.При втором прохождении программы стеки выделяются в отдельную память.

Я не уверен в процессорах на базе CISC, но подозреваю, что они не слишком сильно различаются.

Ваша программа демонстрирует неопределенное поведение именно потому, что:

  • вы не включаете <stdio.h> или <cstdio> в зависимости от языка, на котором вы компилируете свой код, как
  • printf и все функции с переменными аргументами не имеют возможности проверять свои аргументы по типу.Следовательно, с вашей стороны обязательно передавать правильно типизированные аргументы.Тебе действительно следует это сделать:
  • system() область применения не объявлена.Включать в себя <stdlib.h> или <cstdlib> может быть, в зависимости от обстоятельств.

Напишите свой код следующим образом:

   #include <stdio.h>

   int main() {
      /* ... */
      printf("%p %p %p\n", (void *)foo, (void *)&i, (void *)main);
      /* ... */
   }

Также обратите внимание, что:

  • Определение void foo() является прототипом не на C, а на C ++.Однако, если бы вы написали void foo(void) вы бы получили прототип на обоих языках.
  • system() зависит от реализации - ваш код может вести себя не так, как ожидалось, на разных платформах.

Сам язык (C или C ++) не накладывает никаких ограничений на то, как организовать память.В нем даже нет понятия стека или кучи.Они определяются реализациями так, как они считают нужным.В идеале вам следует ознакомиться с документацией, предоставленной вашей реализацией, чтобы получить четкое представление о том, что они делают.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top