作者在用C++解释堆栈和堆时是错误的还是我误读了什么?

rnd*_*809 54 c++ c++11

这是代码:

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;
}
Run Code Online (Sandbox Code Playgroud)

现在这里是作者关于代码的说明:

需要注意的另一点是,通常new使用不同的内存块,而不是我们一直使用的普通变量定义.变量nightspd都将其值存储在称为堆栈的内存区域中,而new分配的内存位于称为堆或自由存储的区域中.


初步问题:

现在我担心的是:变量pd是由关键字new创建的,因此它应该像变量pt一样存储在名为heap的区域中,因为它们都是由关键字new创建的.

我在这里错过了什么吗?非常感谢您的投入.

基于搁置的修订问题/后续行动:

这个问题被5个人搁置了,因为他们无法理解我在问什么.我相信我的问题已经得到了回答,但对于那些仍然不确定我最初要求的人,请继续阅读:

我不清楚作者关于变量及其值存储在内存中的位置的解释.至于作者的解释,我有一种信念,即通过使用关键字new动态创建的任何内存(或者我应该在编译后的运行时说)都存储在堆中而不是堆栈中.

因此,当他写出变量pd具有存储在堆栈中的值时,它让我很困惑,但是如果变量是在"runtime"期间使用关键字new创建的,那么它是如何可能的,所以它应该在堆中,而不是堆栈.

请尝试使用上面的代码作为参考,特别是答案中的**变量(nights,pd和pt),以便从代码的角度理解它.

先感谢您!

Lun*_*din 96

指针变量ptpd存储在堆栈上.它们指向的值(用new分配)存储在堆上.

如果我写一个带有标有"湖"的箭头的标志,那并不意味着标志本身就是一个湖泊,也不是说它必须安装在湖中.相反,它应该安装在坚实的地面上,指向湖的方向.

  • @πάνταῥεῖ那又怎样?如果C++标准没有提到它它不存在?标准中没有提到堆栈和堆的原因仅仅是因为这些事情超出了编程语言的范围.所有真实世界的计算机都使用称为堆栈和堆的段.(虽然当然局部变量不一定存储在堆栈中,但它们可以存储在寄存器中.) (28认同)
  • 挑剔:c ++标准根本没有提到_stack_或_heap_的概念.他们有一个本地和动态存储IIRC的概念. (20认同)
  • 优秀的湖泊比喻.我要偷了! (9认同)
  • c ++的黑暗面是语言警察. (7认同)
  • @πάνταῥεῖ`C++`标准在所有地方都提到了"堆栈"一词."免费商店"只是*C++的术语,用于*堆*,相同的概念,但*堆*是一个通用计算术语,用于获取任意内存块的地方.它与严格的FILO组织的`stack`形成鲜明对比. (3认同)
  • 等等,*所有*真实世界的计算机都使用称为堆栈和堆的段?我很确定至少有一个没有,并且实际上在"堆"上分配的内存链接列表中分配了自动存储变量 (3认同)
  • @Lundin:C++语言不使用堆栈和堆的原因之一是那些是实现细节.如果你在优化的情况下编译这个代码,一个好的编译器将不会使用堆,它只是将另一个双重放在堆栈上(甚至是一个寄存器).因此,描述这种情况的准确方法是*最坏情况*`pd`最终在堆栈上及其在堆上的指向值.强调*最坏情况*. (3认同)
  • @rnd809它们的值(实际变量)都存储在堆栈中.在指针的情况下,值恰好是地址.如果你遵循这些地址,你将最终在其他地方分配的内存中,在这种情况下在堆上. (2认同)
  • 因为,如果符号内的消息大小不超过某个值,则符号永远不会改变大小.然而,湖泊可以是不同的形状和大小 (2认同)
  • @Ludin重要的是要正确,而不是破坏语言的封装.这是抽象的原因.语言不能保证裸机变量的存在.人们应该意识到这一点.打破这种封装导致自定义语言扩展.不同的说法是"那些变量有这样的存储,通常以这种方式实现",而不是"在c ++这里就是这里,而且就在那里".因为它可能不是真的. (2认同)
  • 但通常人们确实需要下面的模型.C++标准内存模型可能更正确(或更不正确!)但它相当干燥和神秘.大多数实际实现实际上都有堆栈和堆. (2认同)
  • @JustinTime是的我应该更清楚标准是指除了类模板之外的程序堆栈.就此而言,还有在标准库中创建`heap`数据结构的函数,但`heap`数据结构是与*heap storage*无关的概念,作为内存的动态资源.我想也许人们会混淆这两者. (2认同)
  • @MatthieuM.我的回答解决了最常见的系统实现._Again:_ C++说或不说的并不重要,因为变量最终存在于内存中的范围超出了编程语言的范围.此外,这是针对需要掌握基本概念的初学者.他们不必关心编译器如何优化堆访问,无堆栈/无堆系统以及其他此类更高级的主题. (2认同)

Bat*_*eba 19

作者唯一的错误是

  1. delete一旦使用指针就不会调用.

  2. 重复使用endl是值得怀疑的.请\n改用.

  3. using namespace std;虽然经常在教程中用来实现简洁,但在生产代码中却是不明智的,特别是在标题中.

  4. int * pt = new int; *pt = 1001;代替使用int* pt = new int(1001);是有问题的,因为作者拥有它的方式,*pt在两个陈述之间处于不必要的未初始化状态.这使得您的代码容易受到不稳定性的影响.

  5. (次要).如果分配失败,您应该始终考虑可能new抛出std::bad_alloc异常的可能性.

我不会担心术语堆栈太多; 它们是语言实现概念,不是语言本身的一部分.首选术语自动动态存储持续时间.这就是C++标准所使用的,那么为什么要发明一整套替代术语呢?

我真的把这本书烧掉了,给自己一份Stroustrup.

  • 我不确定我同意#5.抛出异常(而不是返回错误代码,C风格)的全部意义在于,您没有*为异常编写处理代码.如果尝试分配内存失败 - 这是典型情况下的意外事件,并且您甚至无法编写处理程序,则抛出异常并终止程序. (12认同)
  • 你为什么反对使用endl?这不是一个用于编写符合操作系统的终结点的工具吗?请记住,Windows使用CRLF,因此'\n'不是正确的行尾. (8认同)
  • +关于烧书的最后评论 - 这本书似乎在教授一系列糟糕的习惯,这会使同行评审失败. (6认同)
  • @Benubird` \n`无论操作系统如何都是正确的; 我们有文本模式和二进制模式的部分原因是抽象出行结束差异.(并且所有`endl`都是写一个`\n`后跟一个齐声.) (4认同)
  • 这本书变得更糟.为什么它会在`new`之后引入`delete`,中间有一些代码?除非代码被加水印"绝对垃圾",否则这种行为是应该受到谴责的.如果习惯于垃圾收集者使用语言的民众阅读更是如此. (3认同)

Joh*_*ode 16

图片会有所帮助.

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            
Run Code Online (Sandbox Code Playgroud)

对象nights,pt以及pd所有对象都有auto存储持续时间.在大多数实现中,这意味着它们是从运行时堆栈中分配的.对象nights位于地址0xff0010001并存储该值1001.对象pt住在地址0xff0010004并存储该地址由创建的动态对象的new,这是0x00b0fff0.该对象pd位于地址0xff00100c并存储由其创建的另一个动态对象的地址new,即0x00b0fff4.

堆对象在地址处0x00b0fff0并分别0x00b0fff4存储值100110000001.0.

编辑

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;
}
Run Code Online (Sandbox Code Playgroud)

我得到了输出

       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
Run Code Online (Sandbox Code Playgroud)

在这种情况下,地址真实的.在我的系统(x86_64/Linux SLES-10)上,堆栈地址从高位开始"向下"(向低位地址)增长,而堆地址从低位开始向上增长(朝向更高地址).

x86是小端,意味着被寻址的字节是最低有效字节; 需要从右到左读取多字节对象.


  1. 所有地址都是凭空创建的,并不代表任何实际的实现或架构.


Dav*_*rtz 15

现在我担心的是:变量pd是由关键字new创建的,因此它应该像变量pt一样存储在名为heap的区域中,因为它们都是由关键字new创建的.

不,那不是.代码是:

 double * pd = new double; // allocate space for a double
Run Code Online (Sandbox Code Playgroud)

这与以下内容没有区别:

double * pd;
pd = new double;
Run Code Online (Sandbox Code Playgroud)

很明显,pd它本身不是由new运营商创造的,只有它所拥有的价值.但他在谈论pd,而不是它的价值.

  • 作者正在谈论价值观.所以,是的,`pd`的值是一个地址,它存储在堆栈中.但它的值是一个指向堆上内存的地址. (2认同)

小智 7

pd变量存储在栈存储器,但是其中所述存储器位置pd指向堆内存分配.我想作者想要强调这些概念之间的这种差异.