在 C++ 中针对 char * 正确使用 delete 与 delete[ ]

Proper use of delete vs delete[ ] with respect to char * in C++

我有一段代码:

#include<iostream>

using namespace std;

int main()
{
    char * str = new char;

    cin >> str;
    cout << str;

    delete str;
}

对比

#include<iostream>

using namespace std;

int main()
{
    char * str = new char[30];  

    cin >> str;
    cout << str;

    delete []str;
}

当我使用 STDIN 向任一程序提供输入时,哪个程序可确保没有内存泄漏?

这个疑问是因为我们的教授告诉我们一个 char * 基本上等同于一个 char 数组。因此,如果我像第一种情况一样分配堆内存并让 str 'hold' 一个字符数组,如果我然后删除 str,它会完全删除数组吗?我知道第二种情况成功了。

我已经通过了->

delete vs delete[] operators in C++

delete vs delete[]

因此我知道 delete[] 删除由 new[] 分配的内存并且 delete 对 new 执行相同的操作。但是,如果 new 本身分配了连续的内存位置怎么办??

你的两个第一个代码示例都是错误的。

char * str = new char;  
cin >> str;

您只为单个字符分配了内存。如果您读取除空字符串以外的任何内容,您将写入未分配的内存并具有未定义的行为。

if I then delete str, does it delete the array completely?

它只会删除您分配的一个字符。您在未分配内存中写入的其余字符串不会直接受到 delete 的影响。这不是内存泄漏,这是内存损坏。

对比

char * str = new char[];

这不是合法的 c++。必须指定数组大小。

编辑:修复后,只要您读取的字符串不超过 29 个字符,第二个代码就是正确的。如果您读取更长的字符串,您将再次遇到未定义的行为。

But what if new itself allocates contiguous memory locations?

没有。 new(相对于 new[])分配并构造一个对象。并且 delete 只销毁并释放一个对象。

TLDR 这两个程序都没有内存泄漏,但第一个程序由于内存损坏而出现未定义的行为。

你不能做这样的事情。 字符 *ptr = 新字符; 意味着你已经分配了 sizeof(char) 字节。 如果你将执行 cin >> ptr,并向那里传递超过 1 个字符,你将得到段错误,因为内存未分配。

要分配一个字符数组,您需要按以下方式进行: char *ptr = new char[大小]; 它将分配 size * sizeof(char) 字节。 然后你可以使用 cin >> ptr 来填充数据。

我相信您误解了内存泄漏和缓冲区溢出之间的区别。

什么是缓冲区溢出?

当我们有一些内存要存储一些数据时,就会发生缓冲区溢出。当我们存储该数据时,我们会在其中放置太多数据。例如:

int x[4];
x[0] = 7;
x[1] = 8;
x[2] = 9;
x[3] = 10;
x[4] = 11; // <-- Buffer Overflow!

您的代码显示出潜在的缓冲区溢出,因为 cin 不知道您分配了多少内存。在使用 char * 参数时,没有真正的方法可以告诉它。所以在你的第一个例子中,如果你要写任何比空字符串长的字符串,你会导致缓冲区溢出。同样,如果您要向第二个示例写入超过 30 个字符(包括空字符),您也会导致 缓冲区溢出.

什么是内存泄漏?

一个内存泄漏传统上是这样表示的:

char *x = new char[30];
x[0] = 'a';
x[1] = '[=11=]';
x = new char[10]; // <-- Memory Leak!

此时在代码中,您无法在第一次分配时调用 delete[]。您没有指向该指针的变量。那是 内存泄漏

删除[]有什么作用?

让我们考虑在某个地方有一些桶可以给我们提供内存块。我们可以通过 newnew[] 从那个桶中获取内存块。当我们使用 deletedelete[] 时,我们 return 将那些内存块放回桶中。

我们和newdelete约定的是,一旦我们对一块内存调用delete,我们就不再继续使用它。我们不这样做,因为系统可能会重用那块内存,或者它可能已经完全移除了访问该指针的所有能力。

这怎么可能行得通?

你有这段代码:

char *x = new char;
cin >> x;

我想告诉你,它与这段代码基本相同:

char y;
cin >> &y;

在这两种情况下,您只为 一个 个字符分配了 space。因此,当我们在 x 上调用 delete 时,我们只会删除一个字符。那里的代码可能会中断的部分是 cin 会认为有足够的内存分配给它要尝试写入该指针的任何字符串。

事实是,可能还不够 space。一个字符只有 space。甚至字符串 "a" 也占用 2 个字符。