Не ошибся ли автор в объяснении стека и кучи на С++, или я что-то неправильно понял?
Вот код:
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) в своем ответе, чтобы я мог понять это с точки зрения кода.
Заранее благодарю вас!
Ответы
Ответ 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
, выделяется в кучевой памяти. Я думаю, что автор хотел подчеркнуть именно эту разницу между этими понятиями.