如何在指针初始化为数组的第一个索引后释放指针



试图学习如何使用malloc/calloc/free并弄乱初始化。代码的第一个版本有效,第二个版本给了我堆的损坏。但是,它们都正确打印了数组。当我在初始化指向数组中第一个元素地址的指针后尝试"free(pData)"时,该问题发生在第二个版本中。

谁能解释这种行为?有没有办法清除这样的初始化指针?

工作版本:

#include <iostream>
using namespace std;
int main(){
double * pData;
pData = (double*) calloc (4,sizeof(double));
if (pData==NULL) exit (1);
double uc[] = {10.0, 20.0, 30.0, 40.0};
pData[0] = uc[0];
pData[1] = uc[1];
pData[2] = uc[2];
pData[3] = uc[3];
printf ("You have entered: ");
for (int n=0;n<4;n++) cout << pData[n];
free (pData);
cin.get();
return 0;
}

堆损坏错误:

#include <iostream>
using namespace std;
int main(){
double * pData;
pData = (double*) calloc (4,sizeof(double));
if (pData==NULL) exit (1);
double uc[] = {10.0, 20.0, 30.0, 40.0};
pData = &uc[0];
printf ("You have entered: ");
for (int n=0;n<4;n++) cout << pData[n];
free (pData);
cin.get();
return 0;
}
pData = (double*) calloc (4,sizeof(double));
if (pData==NULL) exit (1);
double uc[] = {10.0, 20.0, 30.0, 40.0};
pData = &uc[0];
printf ("You have entered: ");
for (int n=0;n<4;n++) cout << pData[n];
free (pData);
cin.get();

首先,您将pData分配给calloc提供给您的指针,这是您唯一可以调用free的指针

然后,重新分配pData以指向具有自动生存期的变量的地址,在此过程中丢失calloc给您的地址,然后尝试free该地址。这不是它的工作原理,您必须保留calloc提供给您的地址的副本才能free它:

pData = (double*) calloc (4,sizeof(double));
if (pData==NULL) exit (1);
double uc[] = {10.0, 20.0, 30.0, 40.0};
double* originalData = pData;
pData = &uc[0];
printf ("You have entered: ");
for (int n=0;n<4;n++) cout << pData[n];
free (originalData);
cin.get();

旁注:在C++中使用new/delete而不是malloc/calloc/free

旁注:如果真的有必要,根本不要使用动态分配或使用智能指针。

pData 是一个指针。 这意味着它是一个变量(它自己的内存片段),可以保存另一块内存的地址。

在第一个示例中,当您调用 calloc 时,它会返回内存区域的地址(至少与请求的大小一样大),并将其存储在 pData 中。 然后,通过一次一个地复制 uc 数组中的值来写入该区域。 然后你调用free(pData),它表示你已经完成了使用之前从calloc获得的内存区域。

在第二个示例中,您分配一个内存区域,并像以前一样将其存储在 pData 中。 但是,然后更改存储在 pData 中的内容 - 将其显式设置为保存 uc 数组的内存区域。 当您调用 free(pData) 时,您不是在 calloc 返回的区域中调用 free,而是在分配给 uc 的区域上调用 free。 这将导致未定义的行为 - 在您的情况下,free() 调用可以检测到这不是 calloc 之前返回的内存,并且(大概)记录错误并终止程序。

为了说明,请尝试打印 dData 的内容

printf("pData points to address %pn", pData);

在 calloc() 之后和 free() 之前。 在第二种情况下,您会看到它发生了变化。

在第二种情况下,您正在尝试释放堆栈上的地址!

这会分配 pData 指向 uc数组:pData = &uc[0];

由于 uc 数组位于堆栈上,因此您不得释放它。只允许堆分配/释放

此代码段尝试通过打印地址来显示差异:

#include <cstdio>
#include <iostream>
using namespace std;
int main(){
double * pData;
double uc[] = {10.0, 20.0, 30.0, 40.0};
pData = (double*) calloc (4,sizeof(double));
if (pData==NULL) exit (1);

printf("Heap allocated pointer to: %pn", pData);
printf("Local stack pointer to: %p", uc);
pData = &uc[0];
printf("pData now points to: %p", pData);
printf ("You have entered: ");
for (int n=0;n<4;n++) cout << pData[n];
free (pData);
cin.get();
return 0;
}

我将解释您的第二个代码片段的作用:

double * pData;
pData = (double*) calloc (4,sizeof(double));
if (pData==NULL) exit (1);
double uc[] = {10.0, 20.0, 30.0, 40.0};
pData = &uc[0];
printf ("You have entered: ");
for (int n=0;n<4;n++) cout << pData[n];
free (pData);
  1. 声明指向双精度的指针
  2. 在堆上分配和初始化 4 个双精度块,pData指向块的第一个元素
  3. 如果分配失败,请退出
  4. 声明并初始化 4 个双精度数组,称为uc
  5. pData指向uc的第一个元素 - 泄漏先前分配的块。
  6. 打印一点
  7. 循环遍历pData(现在是uc的别名)并打印元素
  8. 尝试取消分配堆栈上的内存 (uc) - 未定义的行为会导致错误

您尝试做的,也就是第一个代码段成功执行的操作,是将uc复制到pData。如您所见,这不是此代码的作用。


它与"堆的损坏"有什么关系?

C(C++继承自C)具有"堆栈">"堆">的抽象概念。您使用malloccalloc(以及 C++ 中的new关键字)创建的所有内容都位于中,其余所有内容都位于堆栈中。

当您从堆中获取资源时,您有责任释放它。编译器会为您处理堆栈。 当尝试取消分配不在您的管辖范围内的内存时(正如我所说,堆栈是编译器的领域),您已经在其轮子上放置了一根棍子。 于是,一个复杂的失败过程开始了。

释放堆栈上的内存是未定义的行为,这意味着标准没有指定如果这样做应该发生什么。如果你编写了未定义的行为,每个编译器和每台机器都可以自由地做任何事情 - 包括抛出不相关的错误,突然燃烧,甚至正常工作。

这里的关键是指针指向其他某个数据对象。因此,您使用指针执行的操作会影响该数据对象。在第一个示例中,数据对象是在免费存储上分配的数组。完成该数组后,您应该释放它;这就是free(pData)所做的。在第二个示例中,指针最终指向一个数据对象,该对象是堆栈上分配的数组,即它指向uc。由于uc是在堆栈上分配的,因此当代码离开当前范围时,它将被释放。它不是在堆上创建的,因此不得释放它。

相关内容

  • 没有找到相关文章

最新更新