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

时间:2017-05-03 15:48:26

标签: c++ pointers memory malloc

尝试学习如何使用malloc / calloc / 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;
}

5 个答案:

答案 0 :(得分:3)

  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

侧面注意:根本不要使用动态分配或使用智能指针。

答案 1 :(得分:0)

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

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

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

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

  printf("pData points to address %p\n", pData);

在calloc()之后,以及free()之前。在你的第二种情况下,你会发现它发生了变化。

答案 2 :(得分:0)

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

这将pData分配给 point 到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: %p\n", 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;
}

答案 3 :(得分: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. 的指针
  3. 在堆上分配并初始化4个双打的块,使pData指向块的第1个元素
  4. 如果分配失败,请退出
  5. 声明并初始化名为uc
  6. 的4个双打数组
  7. pData指向uc的第一个元素 - 泄漏之前分配的块。
  8. 打印一下
  9. 循环显示pData(现在是uc的别名)并打印元素
  10. 尝试取消分配堆栈中的内存(uc) - 未定义的行为,导致错误
  11. 您尝试做的是第1段成功执行的操作,即将uc复制到pData。如您所见,这不是此代码的作用。

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

    C(C ++继承)具有“stack”“heap”的抽象概念。您使用malloccalloc(以及C ++中的new关键字)创建的所有内容都存储在上,其余部分都位于堆栈< / em>的

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

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

答案 4 :(得分:-1)

这里的关键是指针指向某些其他数据对象。所以你用指针做什么会影响那个数据对象。在第一个示例中,数据对象是在免费商店上分配的数组。当你完成那个阵列时,你应该释放它;这是uc的作用。在第二个例子中,指针最终指向一个数据对象,该数据对象是在堆栈上分配的数组,即它指向uc。由于{{1}}在堆栈上分配,因此当代码离开当前作用域时将释放它。它不是在堆上创建的,因此不能释放它。