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

Не ошибся ли автор в объяснении стека и кучи на С++, или я что-то неправильно понял?

Вот код:

int main()
{
    using namespace std; 
    int nights = 1001; 
    int * pt = new int; // allocate space for an int
    *pt = 1001;         // store a value there

    cout << "nights value = ";
    cout << nights << ": location " << &nights << endl;
    cout << "int ";
    cout << "value = " << *pt << ": location = " << pt << endl;

    double * pd = new double; // allocate space for a double
    *pd = 10000001.0; // store a double there

    cout << "double ";
    cout << "value = " << *pd << ": location = " << pd << endl; 
    cout << "location of pointer pd: " << &pd << endl;
    cout << "size of pt = " << sizeof(pt);
    cout << ": size of *pt = " << sizeof(*pt) << endl;
    cout << "size of pd = " << sizeof pd;
    cout << ": size of *pd = " << sizeof(*pd) << endl;

    return 0;
}

Теперь вот заметка автора о коде:

Еще одно замечание: обычно новый использует другой блок памяти, чем обычные определения переменных, которые мы использовали. И переменные ночей, и pd имеют свои значения, хранящиеся в области памяти, называемой стек, тогда как память, выделенная новой находится в области, называемой кучей или свободным хранилищем.


Начальный вопрос:

Теперь я беспокоюсь об этом: переменная pd была создана по ключевому слову new, поэтому она должна храниться в области под названием куча как переменная pt, так как они были созданы с помощью ключевого слова new.

Я что-то упустил? Заранее благодарю вас за ваши материалы.

Пересмотренный вопрос/последующее наблюдение на основе трюма:

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

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

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

Пожалуйста, попробуйте использовать приведенный выше код в качестве ссылки и, в частности, ** переменных (ночей, pd и pt) в своем ответе, чтобы я мог понять это с точки зрения кода.

Заранее благодарю вас!

4b9b3361

Ответ 1

Переменные указателя pt и pd сохраняются в стеке. Значения, на которые они указывают, выделяются новыми, сохраняются в куче.

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

Ответ 2

Единственными ошибками, которые сделал автор, являются

  • Не вызывать delete после завершения использования указателей.

  • Повторное использование endl вызывает сомнения. Вместо этого используйте \n.

  • using namespace std;, хотя часто используется в учебниках для достижения краткости, не рекомендуется в производственном коде, особенно в заголовках.

  • Использование int * pt = new int; *pt = 1001; вместо int* pt = new int(1001); является сомнительным, так как автор имеет его, *pt находится в излишне неинициализированном состоянии между двумя операторами. Это делает ваш код уязвимым для неустойчивостей.

  • (минор). Вы всегда должны думать о возможности того, что new может генерировать исключение std::bad_alloc, если выделение не выполняется.

Я бы не стал беспокоиться о том, что термины stack и heap слишком много; они представляют собой концепцию реализации языка не часть самого языка. Предпочитают термины автоматического и динамического хранения. Что использует стандарт С++, так зачем придумывать всю нагрузку альтернативной терминологии?

На самом деле я бы сжег книгу и достал себе копию Страустрапа.

Ответ 3

Изображения помогут.

Automatic storage (stack)                      Dynamic storage (heap)
-------------------------                      ----------------------

Item        Address        Value               Address        Value
----        -------        -----               -------        -----          
nights      0xff001000     1001               
    pt      0xff001004     0x00b0fff0 ------>  0x00b0fff0     1001
    pd      0xff00100c     0x00b0fff4 ------>  0x00b0fff4     10000001.0            

Объекты nights, pt и pd имеют продолжительность хранения auto. В большинстве реализаций это означает, что они выделяются из стека времени выполнения. Объект nights живет по адресу 0xff001000 1 и сохраняет значение 1001. Объект pt живет по адресу 0xff0010004 и сохраняет адрес динамического объекта, созданного new, который равен 0x00b0fff0. Объект pd живет по адресу 0xff00100c и сохраняет адрес другого динамического объекта, созданного new, который 0x00b0fff4.

Объекты кучи в адресах 0x00b0fff0 и 0x00b0fff4 сохраняют значения 1001 и 10000001.0 соответственно.

Edit

FWIW, я написал утилиту dumper некоторое время назад, которая выгружает адреса и содержимое объектов; с учетом кода

#include <cstdio>
#include "dumper.h"

using namespace std;

int main( void )
{
  int nights = 1001;
  int *pt = new int;
  *pt = 1001;
  double *pd = new double;
  *pd = 1000001.0;

  char *names[] = { "nights", "pt", "pd", "*pt", "*pd" };
  void *addrs[] = { &nights, &pt, &pd, pt, pd };
  size_t sizes[] = { sizeof nights, sizeof pt, sizeof pd, sizeof *pt, sizeof *pd };

  dumper( names, addrs, sizes, 5, stdout );

  return 0;
}

Я получаю вывод

       Item         Address   00   01   02   03
       ----         -------   --   --   --   --
     nights  0x7fff9efe7c6c   e9   03   00   00    ....

         pt  0x7fff9efe7c60   10   20   50   00    ..P.
             0x7fff9efe7c64   00   00   00   00    ....

         pd  0x7fff9efe7c58   30   20   50   00    0.P.
             0x7fff9efe7c5c   00   00   00   00    ....

        *pt        0x502010   e9   03   00   00    ....

        *pd        0x502030   00   00   00   00    ....
                   0x502034   82   84   2e   41    ...A

В этом случае адреса действительны. В моей системе (x86_64/Linux SLES-10) адреса стека начинаются высоко и растут "вниз" (в сторону более низких адресов), а адреса кучи начинаются с низкого уровня и растут "вверх" (в сторону более высоких адресов).

x86 малозначен, то есть адресный байт является наименее значимым байтом; многобайтовые объекты должны быть прочитаны справа налево.


  • Все адреса составлены из воздуха и не предназначены для представления реальной реализации или архитектуры реального мира.

Ответ 4

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

Нет, это не так. Код:

 double * pd = new double; // allocate space for a double

Это не отличается от:

double * pd;
pd = new double;

Таким образом, pd сам по себе не создается оператором new, а только его значение. Но он говорит о pd, а не о его значении.

Ответ 5

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