GrainOfSalt
GrainOfSalt

Reputation: 66

How to free a pointer after it has been initialized to the first index of an array?

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

Answers (5)

Ivan Rubinson
Ivan Rubinson

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);
  1. Declare pointer to double
  2. Allocate and initialize chunk of 4 doubles on the heap, make pData point to the 1st element of the chunk
  3. If allocation fails, exit
  4. Declare and initialize array of 4 doubles called uc
  5. Make pData point to the first element of uc - leaking the previously allocated chunk.
  6. Print a bit
  7. Loop through pData (which is now an alias of uc) and print the elements
  8. Attempt de-allocating memory that lives on the stack (uc) - Undefined behaviour, causes your error

What 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

Pete Becker
Pete Becker

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

Tezirg
Tezirg

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

dan.m was user2321368
dan.m was user2321368

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

Hatted Rooster
Hatted Rooster

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

Related Questions