Reputation: 66
Trying to learn how to use malloc/calloc/free and messing around with initialization. First version of code works, second version gives me a corruption of heap. However, both of them correctly print the array. The issue occurs in the second version when I try to "free(pData)" after initializing a pointer to the address of the first element in an array.
Can anyone explain this behavior? And is there any way of clearing such an initialized pointer?
Working Version:
#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;
}
Corruption of the heap error:
#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;
}
Upvotes: 1
Views: 200
Reputation: 3339
I'm going to explain what your 2nd snippet does:
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
point to the 1st element of the chunkuc
pData
point to the first element of uc
- leaking the previously allocated chunk.pData
(which is now an alias of uc
) and print the elementsuc
) - Undefined behaviour, causes your errorWhat you were trying to do, which is what the 1st snippet does successfully, is to copy uc
in to pData
. As you can see, this is not what this code does.
What does it have to do with "corruption of the heap"?
C (from which C++ inherits) has an abstract concept of "stack" and "heap". Everything you create with malloc
, calloc
(and the new
keyword in C++) lives on the heap, and all the rest on the stack.
When you get a resource from the heap, it is your responsibility to free it. The compiler takes care of the stack for you. When trying to de-allocate memory that isn't in your jurisdiction (as I said, the stack is the compilers territory), you've placed a stick in it's wheels. So a complicated process of failure has begun.
Freeing memory on the stack is undefined behaviour, which means that the standard does not specify what should happen if you do that. Every compiler and every machine is free to do whatever if you write undefined behaviour - including throwing unrelated errors, suddenly combusting, or even working correctly.
Upvotes: 0
Reputation: 76315
The key here is that a pointer points at some other data object. So what you do with the pointer affects that data object. In the first example, the data object is an array allocated on the free store. When you're finished with that array you should release it; that's what free(pData)
does. In the second example, the pointer ends up pointing at a data object that is an array allocated on the stack, i.e., it points at uc
. Since uc
is allocated on the stack, it will be released when the code leaves the current scope. It was not created on the heap, so it must not be freed.
Upvotes: -1
Reputation: 1639
In the second case, you are trying to free an address on the stack !
This assigns pData to point to the uc array :
pData = &uc[0];
Because the uc array is on the stack you must not free it. Only heap allocation/release are allowed
This snippet attempts to show the difference by printing the addresses :
#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;
}
Upvotes: 1
Reputation: 1705
pData is a pointer. What that means is that it is a variable (its own piece of memory), which can hold the address of another piece of memory.
In your first example, when you call calloc, it returns the address of a region of memory (that is at least as large as the size requested), and stores it in pData. You then write to that region, by coping the values from your uc array, one at a time. You then call free(pData), which says that you are done using the region of memory you previously got from calloc.
In your second example, you allocate a region of memory, and store it in pData like before. However, then you change what is stored in pData - you explicitly set it to the region of memory that holds your uc array. When you call free(pData), you are not calling free on the region returned by calloc, but are calling free on the region that was allocated for uc. That will result in undefined behavior - in your case, the free() call can detect this wasn't memory returned by calloc previously and (presumably) logs an error and terminates the program.
To illustrate, try printing the contents of dData
printf("pData points to address %p\n", pData);
after the calloc(), and before the free(). In your second case, you'll see that it changed.
Upvotes: 1
Reputation: 36483
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();
First, you assign pData
to a pointer given to you by calloc
, this is the only pointer you're allowed to call free
on.
You then re-assign pData
to point to the address of a variable with automatic lifetime, in the process losing the address that calloc
gave you, and then try to free
that. That's not how it works, you have to keep a copy around of the address that was given to you by calloc
to be able to free
it:
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();
Side note : use new
/ delete
in C++ instead of malloc
/ calloc
/ free
.
Side side note : Don't use dynamic allocation at all or use smart pointers if you really have to.
Upvotes: 3