0

当您尝试删除非指针类型但它没有显示并且仅在运行时崩溃时,人们会期望编译器错误。我浪费了整整 2 天的时间,以为它是一个实际的指针变量,并且由于代码中其他地方的堆损坏而崩溃。最后我无意中查看了被删除变量的声明,发现它根本不是指针类型。

4

4 回答 4

7

最后我无意中查看了被删除变量的声明,发现它根本不是指针类型。

这是错误的——当你试图删除一个不是指针类型的变量时,编译器确实会给你一个错误:

$ cat test.cpp
int main() {
    int a;
    delete a;
}
$ g++ test.cpp
test.cpp: In function 'int main()':
test.cpp:3:12: error: type 'int' argument given to 'delete', expected pointer
     delete a;
            ^

但是,编译器接受任何delete可以隐式转换为指针的类型。这可以说是我们想要的——隐式转换意味着我们想要“把这个变量当作一个指针来对待”。另一方面,在 的上下文中可能永远不会出现这种情况delete,并且 C++ 语言在此可能更具限制性。

也就是说,确定一个指针是否指向有效的堆分配内存是不可能的——这相当于解决了停机问题,因此在一般情况下被证明是无法解决的。

于 2013-07-10T07:34:06.827 回答
3

(稍微重新解释您的问题意味着在尝试删除堆栈分配的内存时警告您:)

对于编译器来说,这将是极其困难的,如果不是不可能的话。例如,如果指针被传递到不同的编译单元怎么办?甚至是不同的 dll /so?

我相信这个网站上所有的老猫都能理解你的沮丧,但是,我敢打赌你不会再犯同样的错误了!

于 2013-07-10T07:33:09.370 回答
1

如果您正在考虑这样的事情:

int a;
int *pa = &a;
delete pa;

然后你不会得到编译器错误,因为上面的代码虽然错误,但根据语言定义并不是非法的。但是,如果情况足够明显,编译器可能会发出警告(我没有尝试,但我想不会)。静态分析器工具应该可以检测到这一点。

但是,如果你有这样的东西,编译器就不会知道有什么问题:

void foo(int* pa)
{
    delete pa;
}
...
int a;
foo(&a);

要检测到这一点,您肯定需要一个好的静态分析器,例如 Coverity。但通常你应该避免这样的模式。删除指针应始终由创建它的类(或其他实体)负责。更好的是,你应该使用智能指针而不是手动调用 new/delete(你需要一个兼容 C++11 的编译器或使用 Boost)。

于 2013-07-10T07:44:56.217 回答
0

我认为您的意思是您调用delete了指向堆栈上值的指针。在堆栈上有指向堆的指针是完全合法的,你可以这样做delete

使用像valgrind这样的好工具会发现你有一个错误的删除:

Valgrind 用户手册:非法释放

于 2013-07-10T07:42:45.083 回答