Link to home
Start Free TrialLog in
Avatar of Member_2_2394978
Member_2_2394978Flag for United Kingdom of Great Britain and Northern Ireland

asked on

Reuse pointer after free() -- double free error

Hi *,

I think it is safe to reuse pointers after freeing them (right?). For exmaple,
float* t = (float*)calloc(100, sizeof(float));
free(t);
t = (float*)calloc(100, sizeof(float));
free(t);

Open in new window

works fine.

EDIT: that is a lie, it also gives a double free. Why?

However, I get a double free error from
float** v;
v = Get_2D_FloatArray(10, 10);
Free_2D_FloatArray(v);
v = Get_2D_FloatArray(10, 10);
Free_2D_FloatArray(v);

Open in new window


where

int** Get_2D_Int-Array(int dim1, int dim2) {
  int** v;
  v = (int**)calloc(dim2, sizeof(int*));
  v[0] = (int*)calloc(dim1*dim2, sizeof(int));
  int i=0;
  for (i=1; i<dim1; i++) {
    v[i] = v[i-1] + dim2;
  }
  return v;
}

void Free_2D_int_Array(int** v) {
  free(v[0]);
  free(v);
  v[0] = NULL;
  v = NULL;
}

Open in new window


Any suggestions?
Cheers,
James
Avatar of evilrix
evilrix
Flag of United Kingdom of Great Britain and Northern Ireland image

>> I think it is safe to reuse pointers after freeing them (right?).
A pointer is just a data-type, it can be re-assigned a value at any time (regardless of whether you free malloc/calloc allocated memory to it or not). The only requirement if you allocated from the heap is that at some point you free it.... but it don't have to be via the original pointer that the address was assigned too.

If you are getting a double free error you must be calling free on the same address more than once.
Hi James,

You can always reuse pointers.  As far as the program is concerned, they are just variables.  (Their contents just happens to be an address in memory instead of a "normal"item like an integer or character.)

It's a good idea to set the pointer to 0 (NULL) after freeing it.  This makes it easy for the program to track valid/obsolete pointers.  Most free() functions disregard the attempt to free memory when the passed value is NULL so if the program does try to free the same memory twice, having set the pointer to NULL after the first call to free() gets you past the error.


Good Luck,
Kent
Avatar of Member_2_2394978

ASKER

Thanks for quick responses.

I thought it was safe to re-use them. As you say they are just variables.

So, why does my code produce a double free?

Cheers,
James
Hi James,

What are you seeing that says "double free"?


Kent
>> As you say they are just variables.

Indeed, variables that are specifically designed to hold address values just as (for example) an int is specifically designed to hold a numeric value.

I can see Kent is here so I'll leave you guys to work on this.

Good luck.
Avatar of phoffric
phoffric

You must not deference a pointer after freeing it. Swap two lines.
void Free_2D_int_Array(int** v) {  
  free(v[0]);    // OK
  free(v);       // you free v here
  v[0] = NULL;   // now you are dereferencing v after freeing it
  v = NULL;  
}

Open in new window

Hmm ok that makes sense.

I have updated my get function to the attached, to check whether I am allocating memory okay, and I am.

Kent: on my second call to Free_2D_FloatArray(v); (line 5 in my first code attachement in first post) I get
*** glibc detected *** double free or corruption (out): 0x ... *

phoffric: I still get my double free error with both the NULL lines commented. But I would like them in, so how could I reorder them?

Cheers,
James
int** Get_2D_Int-Array(int dim1, int dim2) {
  int** v;
  v = (int**)calloc(dim2, sizeof(int*));
  if (v == NULL) {
    return NULL;
  }
  v[0] = (int*)calloc(dim1*dim2, sizeof(int));
  if (v[0] == NULL) {
    return NULL;
  }
  int i=0;
  for (i=1; i<dim1; i++) {
    v[i] = v[i-1] + dim2;
  }
  return v;
}

void Free_2D_int_Array(int** v) {
  free(v[0]);
  free(v);
  v[0] = NULL;
  v = NULL;
}

Open in new window

If you swap lines 20 and 21, then that should help.
This way you free v[0], and then set v[0] to NULL. So now you are dereferencing v before you free it - OK.

Then you free v, and then set v to NULL - OK.
Thanks.

I have done this, however am still getting the double free problem.

Cheers,
James
That is a lie ... I now get free(): invalid next size (fast) on the second Free_ ...
What I wrote is a fix to a definite problem that you have, but I just realized that you commented out had commented out lines 21 and 22, so you have more than one problem.

You may have other corruption issues in your larger program, but as a test, could you see if this standalone program runs without error.

#include <stdlib.h>

int** Get_2D_Int_Array(int dim1, int dim2) {  
  int** v;  
  v = (int**)calloc(dim2, sizeof(int*));  
  v[0] = (int*)calloc(dim1*dim2, sizeof(int));  
  int i=0;  
  for (i=1; i<dim1; i++) {  
    v[i] = v[i-1] + dim2;  
  }  
  return v;  
}  
  
void Free_2D_int_Array(int** v) {  
  free(v[0]);  
  v[0] = NULL;  
  free(v);  
  v = NULL;  
}

int main()
{
   int** v;  
   v = Get_2D_Int_Array(10, 10);  
   Free_2D_int_Array(v);  
   v = Get_2D_Int_Array(10, 10);  
   Free_2D_int_Array(v);
}

Open in new window

I've found it ...
it works as long as the dimensions are the same both times ... so 10, 10 and 10, 10, however I am (my fault, I didn't include it in here) doing 10, 10 the first time and 1000, 10 the second time. Does this make a difference?

The simple example you gave works for, as I say, the same dimensions, but not for different ones.

Cheers,
James
Hi James,

The size doesn't matter, as long as the program only references the memory in the current size.  That is if you allocate 1,000 bytes, free it, allocate 1,000,000 bytes, free it, and the allocation 5,000 bytes, byte 100,000 is valid only between the 2nd and 3rd allocations.


Kent
In my earlier post, I just immediately noticed a pointer problem which is now cleared up.

>>  I am doing 10, 10 the first time and 1000, 10 the second time. Does this make a difference?
The problem is not that you are doing these two things in succession. You have a problem even if you do not do 10, 10. Just do 1000, 10.

Now, what debugger do you have available? Do you have one that can show multiple memory regions? That would help you.

This single line results in erroneous code (which may or not show up as an immediate error - platform dependent):
   v = Get_2D_Int_Array(1000, 10);

Open in new window

In Get_2D_Int_Array, dim1 is 1000 and dim2 is 10. Here you are allocating a region suitable for 10 pointers.
    v = (int**)calloc(dim2, sizeof(int*));

Open in new window

But then here you are setting 1000 pointers:
     for (i=1; i<dim1; i++)
          v[i] = v[i-1] + dim2;

Open in new window

which goes beyond the allocated region.
The main problem, then, is not freeing twice, but overwriting memory that was not allocated to you.

When dealing with memory allocation errors, the actual errors reported can be very misleading, and the detection of a problem will vary from one system to another. In fact, it is possible that some errors will not be immediately detected until you make a small change to a different part of the program.

Now doing this probably will not cause an error:
   v = Get_2D_Int_Array(10, 10);  
   Free_2D_int_Array(v);  
   v = Get_2D_Int_Array(10, 1000);  
   Free_2D_int_Array(v);

Open in new window

I did notice one other interesting syntactical issue. In Free_2D_int_Array, you set v to NULL; but v is an auto variable in Free_2D_int_Array's stack. So, when you return to main, the v there should not be affected.

You should consider another design for dynamically creating 2d arrays. Here is one approach:  http://rdsrc.us/IApazh

You can get a good deal of useful practical information from this EE search: http://rdsrc.us/CxPzLP
Often, we see a 2d matrix described as nRows x nCols, which would correspond to your dim1 and dim2, respectively. Then to set up the matrix using your code, first allocate enough space for nRows row pointers. So, v[ i ] are pointers to the start of each row. You can allocate v[0] as you have done. But, in the loop, you have to set the remaining (nRows - 1) row pointers.
Morning (here),

Many thanks for your responses.

I have swapped dim1 and dim2, and indeed that did fix my error, and now I have the attached (I also fixed my automatic variable problem, I think?)

I used this approach over the one you linked for less allocations and more contiguous memory (this is related to a previous question, where I must have change the 3d version to a 2d version incorrectly, i.e. having dim1 and dim2 around the wrong way: previous question.

But it is still "double freeing" randomly. Some times it works fine, other times not, other times fine.

Firstly, I suppose, is the code attached fine now? I may be doing something somewhere else (although i'm pretty sure not).

I have gdb available, will that help? I must admit the only thing I can do with it, is put breaks in and evaluate variables!

Cheers,
James
int** Get_2D_Int-Array(int dim1, int dim2) {
  int** v;
  v = (int**)calloc(dim1, sizeof(int*));
  if (v == NULL) {
    return NULL;
  }
  v[0] = (int*)calloc(dim1*dim2, sizeof(int));
  if (v[0] == NULL) {
    return NULL;
  }
  int i=0;
  for (i=1; i<dim2; i++) {
    v[i] = v[i-1] + dim2;
  }
  return v;
}

void Free_2D_int_Array(int*** v) {
  free(*v[0]);
  free(*v);
  *v[0] = NULL;
  *v = NULL;
}

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of phoffric
phoffric

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
in line 12 the for loop should go from 1 to dim1 - 1 (and not dim2) cause you allocated dim1 "rows".

in line 20 and 21 you shouldn't dereference cause the pointers were freed.

look at phoffric's code it is fine. or omit the setting to NULL. it makes not so much sense cause it is local variables only.

Sara

Apologies for the sever delay.

Many thanks,
James