尝试学习如何使用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;
}
答案 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);
pData
指向块的第1个元素uc
pData
指向uc
的第一个元素 - 泄漏之前分配的块。pData
(现在是uc
的别名)并打印元素uc
) - 未定义的行为,导致错误您尝试做的是第1段成功执行的操作,即将uc
复制到pData
。如您所见,这不是此代码的作用。
它与“堆的损坏”有什么关系?
C(C ++继承)具有“stack”和“heap”的抽象概念。您使用malloc
,calloc
(以及C ++中的new
关键字)创建的所有内容都存储在堆上,其余部分都位于堆栈< / em>的
当您从堆中获取资源时,您有责任将其释放。编译器会为您处理堆栈。 当试图取消分配不属于您所在司法辖区的内存时(正如我所说,堆栈是编译器的领域),您已经在它的轮子中放置了一根棍子。 因此,一个复杂的失败过程已经开始。
释放堆栈中的内存是未定义的行为,这意味着标准没有指定如果这样做会发生什么。如果你编写未定义的行为,包括抛出无关的错误,突然燃烧,甚至正常工作,每个编译器和每台机器都可以自由地做任何事情。
答案 4 :(得分:-1)
这里的关键是指针指向某些其他数据对象。所以你用指针做什么会影响那个数据对象。在第一个示例中,数据对象是在免费商店上分配的数组。当你完成那个阵列时,你应该释放它;这是uc
的作用。在第二个例子中,指针最终指向一个数据对象,该数据对象是在堆栈上分配的数组,即它指向uc
。由于{{1}}在堆栈上分配,因此当代码离开当前作用域时将释放它。它不是在堆上创建的,因此不能释放它。