C/C++指针问题

5

简而言之 - 请详细解释下面第一个代码片段中的4条评论。具体来说,deref是什么意思?

我是一名长期从事Java开发的人,想要学习C ++。我偶然发现了这个网站,它针对和我一样的开发人员。

   int x, *p, *q;
   p = new int;
   cin >> x;
   if (x > 0) q = &x;
   *q = 3;                 // 1. deref of possibly uninitialized ptr q
   q = p;
   p = new int;            // 2. potential storage leak (if x != 0 this
                           //     memory will not be returned to free storage)
   *p = 5;
   delete q;
   *q = 1;                 // 3. deref of deleted ptr q
   q = p;
   if (x == 0) delete q;
   (*p)++;                 // 4. deref of possibly dangling ptr p (if x is zero)

尽管我认为我理解指针的工作原理,但我发现很难理解这些注释。
我的理解:
1.我们要么将x(&*q)赋值为3,要么如果q!=&x,则q具有初始值,因为它未初始化,我们刚刚将一个随机内存片段分配给了值3。我不确定如何引用未初始化的内容?
2.这个没问题
3.删除指针后,解引用已删除的指针有什么问题?* q毫无意义吗?
4.悬挂指针有什么问题?既然我们已经删除了它,即使我们仍然拥有指向它的指针,该内存是否适用于重新分配?
我想我的基本误解就在于只声明int指针,这样是否也会分配内存?它在堆栈上还是堆上?
另外,解引用是否只是“读取指针地址处的值”?我觉得我困惑的是,我将其解释为丢失对某些数据的引用,例如;
int *x;
x = new int;
*x = 5;
x = new int; // Dereferencing the first bit of memory allocated.

感谢您的耐心等待,我希望这个问题有些意义。Gav

指针可能出现的所有问题的良好总结。 - Matthieu M.
另一方面,变量名使得跟随逻辑变得不可能。真正的代码从来不会远离这么复杂(或者说,不应该这样)。 - Mooing Duck
5个回答

6
简单来说:指针是一个变量的地址,你可能对其值感兴趣。解引用是访问该值的行为——通过在指针变量前加上 *(解引用运算符)。访问可以是读取、写入或两者兼有。
1. 如果您未将指针初始化为有效地址(或特殊值 NULL)- 您不知道该变量包含什么。解引用将尝试获取其中的任何内容并将其视为地址。这是未定义的行为-所有赌注都关闭,任何事情都可能发生,但如果您很幸运,您将遇到陷阱/硬件异常。 2. 正确。因为 p 保存了您分配的某些内存的地址。不释放系统资源会导致泄漏。解除已删除指针的引用与解除未初始化指针的引用相同-您不知道它可能包含什么值。 3. 再次,未定义的行为。 4. 真的。对于 x==0,您会遇到未定义的行为。悬空指针很危险,因为它们会在最不适当的时候出现,格式化硬盘并永远不再见。这变得无法合理地证明您的程序将如何运作。

悬挂指针也指向你将永远无法再次访问的内存。 - ALOToverflow
你提到的关键点是第二句话——我不知道“dereferencing”这个术语意味着访问存储在变量地址中的值。我以为它的意思是类似于“取消引用”——删除对内存块的最后一个引用,而不是删除它。 - gav

3

在下面添加了更多的注释:

   int x, *p, *q;
   p = new int;
   cin >> x;
   if (x > 0) q = &x;
   *q = 3;                 // <--- 1. deref of possibly uninitialized ptr q
                           // 
                           // q has never been set or is set to point at a non
                           // allocated memory location (if x > 0)
                           //
                           // If x is zero q is uninitialized (has random value).
                           // Therefore de-referencing it to set what it points at 
                           // to 3 has undefined behavior.

   q = p;
   p = new int;            // <--- 2. potential storage leak (if x != 0 this
                           //     memory will not be returned to free storage)
                           // 
                           // This comment is not true. The value pointed at by p
                           // was transferred to q before p was re-assigned. Thus 
                           // there is no potential memory leak. Both p and q are 
                           // now valid pointers.
   *p = 5;
   delete q;
   *q = 1;                 // <--- 3. deref of deleted ptr q
                           // 
                           // In the line above you returned the memory pointed at 
                           // by q back to memory management routines (via delete). 
                           // Technically q still points at the memory but you no 
                           // longer own that memory and thus writing to it has
                           // undefined behavior (because the memory management 
                           // system could have unloaded that chunk from virtual 
                           // memory or re-used it some other way).
   q = p;
   if (x == 0) delete q;
   (*p)++;                 // <--- 4. deref of possibly dangling ptr p (if x is zero)
                           // 
                           // q is reassinged to have the same pointer value as p.
                           // Then If x is zero we delete the pointer q (thus 
                           // returning it to the memory management pool). Since p 
                           // and q are the same pointer they both become invalid at
                           // point. Thus de-referencing p is undefined behavior if
                           // x is zero (because the memory was returned (via delete)

1 我们要么将x(& *q)赋值为3,要么如果q != &x,则q具有刚刚初始化的值,并且我们刚刚将一个随机内存块分配给了值3。我不确定如何引用未初始化的内容?

请参见以下内容:

3 删除指针后引用它会出现什么问题?在'delete q'之后,*q是无意义的吗?

该内存属于其他人(内存管理(或在线程应用程序中,它可能已被重新分配给另一个线程)。写入不属于您的内存具有未定义的行为。

4 悬空指针有什么问题?既然我们仍然拥有指向它的指针,那么现在这个内存是否可用于重新分配?

与3相同的评论。(保留无效指针并不意味着任何事情)

此外,dereference只是意味着“读取指针地址处的值”吗?我认为我感到困惑的是,我将其解释为失去对某些数据的引用,如下所示;

是的:这就是它的全部含义。
但是,如果您不拥有该内存,则可能会发生不良事情。

  • 如果指针指向未映射到物理内存的位置,则会出现某种形式的段错误。
  • 如果内存属于内存管理例程,并且您开始写入随机值(如上述3),则可以破坏内存管理结构,从而导致在进行任何内存管理时程序崩溃。
  • 如果内存属于堆栈,并且您开始写入随机值(如上述3),则可能会覆盖另一个随机变量或函数退出时使用的返回地址。

2
如果你不知道“底层”发生了什么,这将是一个困难的主题。C ++是一种“裸机”编程语言,很少会使任何东西安全。
查看您提供的代码和标记的重点:
第一行有:int x,*p,*q;它在堆栈上声明并定义了一些变量,但没有初始化它们。 C ++也不会在运行时记住这一点。您可能会在编译时得到一些警告,但否则必须在头脑中跟踪事物。
因此,在第1行,我们不知道q是否为&x。如果q尚未初始化,则它可以指向任何地方,但是C ++不知道这一点,并将尝试将值3写入某个内存位置。这一点的答案是C ++不跟踪指针值。
在第3行,再次尝试将整数值写入某个内存位置,但这次是在堆上,在多线程程序中,该内存可能已被重新分配。因此,不幸的是,q*q保留了含义,但不太可能是我们想要的。
在第4行,与3相同的问题,但仅当(x == 0)时,并且是的,尽管我们已删除它,但内存可能已被重新分配。
声明int指针仅为指针本身和堆栈分配内存。
解除引用指针意味着访问指针指向的内存,以进行读取或写入。
关于您的第二段代码:
int *x;         <-- Declares a pointer and allocates it on the stack
x = new int;    <-- Allocate a new int on the heap and remember its address in x
*x = 5;         <-- Overwrite the new int on the heap.
x = new int;    <-- Another allocation and remember its address in x
                    Now we have forgotten where the first allocation was

解引用指的是读取或写入指针所指向的内存。如果您只是读取或写入指针本身,则始终是安全的。这就是为什么您可以传递一个null指针,并且只有在第一次对其进行解引用时才会遇到问题。

2
在:
你通过对指向具体内存地址的指针q进行取消引用请参阅指针取消引用文档,为其赋值。然而,由于if条件将其设置为x的地址,如果x <= 0,则它可能指向随机内存单元(未初始化)。
在上面的行中,你将q设置为指向与p相同的内存地址。然后为p分配新内存。然后你为p所指向的内存分配新内存。
在上一行中,你删除了q指向的内存分配。该内存地址现在可供系统使用。之后,你给不属于你的内存赋值。
如果x == 0,则返回到q和p都指向的系统内存。再次尝试使用不属于你的内存。如果x!= 0,则没有delete p-因此内存未归还给系统。

1

回答您的问题:

  1. x 的值是从流中读取的。这可能失败,导致 x 未初始化(存储垃圾值)。因此,if 的行为不可靠,因此它可能不会将地址分配给 q。因此,赋值 *q = 3 将写入随机内存地址。

  2. 这样做是不好的!没有垃圾回收。第一个对象在堆上分配,并将其地址分配给 p。现在,您分配了第二个对象并覆盖指针以持有新地址。旧地址丢失了,因此没有人可以通过 p 删除它。之后有一个 delete q,它是从 p 复制的地址,但该语句依赖于 x 的值(不可靠)。

  3. 被删除的不是指针,而是所指向的对象。指针仍然持有指向曾经是有效对象的值,但现在是垃圾。如果您对其进行解引用,则程序将具有“未定义行为”。

  4. 是的。运行时系统(如此之小)没有跟踪指向对象的指针。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接