为什么我可以执行 "delete p;",但不能执行 "delete (p+1);"?为什么 delete 需要一个左值?

4
这个页面上,写道:

一个原因是delete的操作数不需要是lvalue。考虑:

delete p+1; 
delete f(x); 

这里,delete的实现没有指针可以分配为零。

将数字添加到指针中,会使指针向前移动那么多个sizeof(*p)单位。

那么,delete pdelete p+1之间有什么区别,为什么只将指针设置为0会成为delete p+1的问题?


Strstroup 建议 C++ 编译器在指针是左值时将其设置为 NULL。我个人认为,一些非空常量更适合用于查找双重删除错误,但即便如此... - Joshua
1
因为加法的结果是一个rvalue,而你不能赋值给一个rvalue。同样的道理,你也不能说(1 + 1) = 4。 - GManNickG
@GMan:那int *a = new int(10); *(a + 1) = 5;呢?这里p+1也会是一个指针。 - Lazer
这不是关于分配指针目标(刚被释放),而是关于重定向指针本身。 - Ben Voigt
在你的代码中,a+1 是一个 rvalue,但是 *(a+1) 是一个 lvalue,这就是你所分配的。Stroustrup 谈论的是 delete 将指针清零,而不是指向的对象。 - Steve Jessop
4个回答

9
您不能执行 p + 1 = 0。同样的道理,如果您执行 delete p + 1,那么 delete 就无法将其操作数(p+1)清零,这就是 Stroustrup FAQ 中提到的问题所在。
虽然您在程序中几乎不可能编写 delete p+1,但这并不重要...

@Steve Jessop:所以,这与p+1没有使用new()分配无关? - Lazer
1
在Stroustrup将其作为FAQ问题的示例时,它并不是有效的。实际上,在标准C++中,如果p+1的结果是由new()分配的地址,因为p是从该地址减去1得到的,那么p就不是一个有效的指针,即使对其进行加1也是未定义行为。这不是你期望依赖的东西。所以这个例子并不现实 - Michael Mrozek的p-1更好。我认为Stroustrup只是选择了p+1作为一个左值的示例,而不是实际删除的示例。 - Steve Jessop
“rvalue的例子”,我的意思是。哎呀。 - Steve Jessop

5

正如您所说,p和p+1指向不同的位置,它们之间相隔sizeof(*p)个单位。您不能删除未分配的内容,但例如:

A* p = new A();
p++;
delete p-1;

如果删除原始分配,将释放内存。当p+1没有被原始分配时,删除p+1是未定义的;glib会出现错误:

*** glibc detected *** free(): invalid pointer: 0x0804b009 ***

实现不能将p+1归零,因为p+1不是一个可修改的变量。该段落的意思是

delete p;

可以翻译为

free(p);
p = 0;

使用p+1没有意义。


你的答案的第一部分与“p+1”是否为有效指针无关。关键是它不是左值。 - nobody
1
什么?他问了两个问题,第一个是“delete p和delete p+1有什么区别?” - Michael Mrozek
@Andrew Medico:它不是lvalue,是因为我们没有使用new()为其分配内存,还是由于完全不同的原因? - Lazer
1
@eSKay 它们是不相关的。L值是具有地址的值,因此可以出现在等式的左侧。p具有地址,因为它是一个变量。而p+1没有地址; p所指向的值会与1相加,而该临时值被内联使用,但它没有永久地存储在内存中。 - Michael Mrozek

1

实际上,您可以删除一个右值。规范(n3242)中没有任何排除它的内容。在实践中,它不会报错

顺便说一下,如果文章说

delete的操作数不需要是左值

这意味着它可能是左值,也可能不是左值。它并没有说操作数不能是左值。


0

原因是它们都不是lvalue,因此将它们设置为NULL(或任何其他值)根本不可能。


1
f(x)可能是一个左值,但不一定是。如果f返回某种类型T的T*&,那么它就是一个左值。或者它可能返回T*,这种情况下它是一个右值。 - Steve Jessop

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