operator "delete[]": 如何检测?误用 "delete" 时的行为?
operator "delete[]": how to detect? Behavior if misuse with "delete"?
我使用的是 Visual Studio 2008 并编译了以下代码。
代码 1:
int* pI = new int[3];
delete pI;
代码2:
int* pJ = new int[3];
delete[] pJ;
很明显,Code1是错误的,因为pI是用"operator new[]"分配的。
那么问题来了:
- 编译器无法选择这个错误(太糟糕了),这只是VS2008的行为吗? Visual Studio、g++、clang 的更高版本如何?
如果我在项目中使用"Code1",
- 它会破坏堆吗(哪种破坏)?
- 未定义的行为还是已定义的行为?
一些带有静态分析工具的编译器可以检测到 static 事件,例如您代码中的事件,这些事件永远无法正常运行。但他们无法在所有情况下检测到所有这些错误。特别动态的案例。
Will it corrupt the heap (what kind of corrupt)?
未定义,因为未定义错误删除数组的行为。它可能工作得很好。它可能会浪费内存。它可能会破坏堆。它可能会使您的程序崩溃。
这就是 "undefined behavior" 的意思。
首先:不要这样做。只是不要。本质上 永远不会 有充分的理由使用 new
的数组形式(对于反对者:不,即使在编写集合 class 时也不行)。我已经 20 多年没有在实际代码中使用过(在那之前不应该使用,但当时没有意识到)。通常,您应该使用 std::vector
。
然后是简短的回答:我不知道有哪个编译器可以对此进行诊断(至少是可靠的)。 When/if 你这样做了,你得到了未定义的行为,这基本上意味着你违反了你与编译器的合同,编译器取消了它的所有义务。
至于在实践中可能发生的情况:这似乎因编译器而异。在某些情况下,您的内存块将在没有 运行 包含它包含的对象的析构函数的情况下被释放(在您的情况下无关紧要,因为 int
的 dtor 基本上是一个 nop)。在其他情况下,代码会崩溃并烧毁。例如,考虑以下代码:
#include <iostream>
#include <iomanip>
struct foo {
~foo() { std::cout << "~foo()\n"; }
};
int main() {
foo *g = new foo[5];
delete g;
}
对于 VS 2015 或 g++ 5.3,这会崩溃并烧毁(即弹出一个对话框,告诉您程序已停止工作)。
对于一些较旧的编译器,析构函数只会 运行 一次,而不是销毁所创建对象所需的 5 次。
我使用的是 Visual Studio 2008 并编译了以下代码。
代码 1:
int* pI = new int[3];
delete pI;
代码2:
int* pJ = new int[3];
delete[] pJ;
很明显,Code1是错误的,因为pI是用"operator new[]"分配的。
那么问题来了:
- 编译器无法选择这个错误(太糟糕了),这只是VS2008的行为吗? Visual Studio、g++、clang 的更高版本如何?
如果我在项目中使用"Code1",
- 它会破坏堆吗(哪种破坏)?
- 未定义的行为还是已定义的行为?
一些带有静态分析工具的编译器可以检测到 static 事件,例如您代码中的事件,这些事件永远无法正常运行。但他们无法在所有情况下检测到所有这些错误。特别动态的案例。
Will it corrupt the heap (what kind of corrupt)?
未定义,因为未定义错误删除数组的行为。它可能工作得很好。它可能会浪费内存。它可能会破坏堆。它可能会使您的程序崩溃。
这就是 "undefined behavior" 的意思。
首先:不要这样做。只是不要。本质上 永远不会 有充分的理由使用 new
的数组形式(对于反对者:不,即使在编写集合 class 时也不行)。我已经 20 多年没有在实际代码中使用过(在那之前不应该使用,但当时没有意识到)。通常,您应该使用 std::vector
。
然后是简短的回答:我不知道有哪个编译器可以对此进行诊断(至少是可靠的)。 When/if 你这样做了,你得到了未定义的行为,这基本上意味着你违反了你与编译器的合同,编译器取消了它的所有义务。
至于在实践中可能发生的情况:这似乎因编译器而异。在某些情况下,您的内存块将在没有 运行 包含它包含的对象的析构函数的情况下被释放(在您的情况下无关紧要,因为 int
的 dtor 基本上是一个 nop)。在其他情况下,代码会崩溃并烧毁。例如,考虑以下代码:
#include <iostream>
#include <iomanip>
struct foo {
~foo() { std::cout << "~foo()\n"; }
};
int main() {
foo *g = new foo[5];
delete g;
}
对于 VS 2015 或 g++ 5.3,这会崩溃并烧毁(即弹出一个对话框,告诉您程序已停止工作)。
对于一些较旧的编译器,析构函数只会 运行 一次,而不是销毁所创建对象所需的 5 次。