2

When I dynamically allocate a structure, and then try to free it, it seems to reallocate it.

typedef struct OBJ_T
{
   int param1, param2;
} OBJ;

OJB* Construct(int par1, int par2)
{
   OBJ* x = malloc(sizeof(OBJ));
   x->param1 = par1;
   x->param2 = par2;
   return x;
}

void freeup(OBJ *x)
{
   printf("x pointer -> %d ", x);
   free(x);
   x=NULL;
   printf("\nx pointer -> %d\n", x);
}

int main()
{
   OBJ foo = *Construct(10, 20);
   freeup(&foo);
   freeup(&foo);
   return 0;
}

The result is:

x pointer -> 2752216
x pointer -> 0
x pointer -> 2752216
x pointer -> 0

So after freeing and nulling out the pointer, if I check it again, it goes back to what it was. Is that a problem? Or is it free and I don't have to worry about it anymore?

  • 4
    The pointer abuse group will be wanting a word with you. On a serious note, you are losing the original pointer already in the line `OBJ foo = *Construct(10,20)`. – MicroVirus Sep 23 '15 at 13:41
  • Seems you have a Java or Delphi background? So there is something to learn about pointers. – Wolf Sep 23 '15 at 13:45
  • It is not possible to call `free()` on memory allocated on the stack: `freeup(&foo);` – alk Sep 23 '15 at 13:48
  • @alk but seems *possible*, It's clear: you should never do this. Can you describe what happens here? UB? – Wolf Sep 23 '15 at 13:55
  • 1
    @Wolf: "It **seems** possible." Is a **very** weak argument in C. – too honest for this site Sep 23 '15 at 13:57
  • Sure, calling `free()` on anything not a null-pointer or allocated by `malloc/calloc/realloc` invokes the infamous Undefined Behaviour, after which anything can happen. @Wolf – alk Sep 23 '15 at 13:58
  • Note: `OBJ* x` is somewhat missleading when read. move the space before the `*`: `OBJ *x`. See this construct `int* ip, i;` remember: `i` will **not** be a pointer as the visual binding of the `*` to `int` implies. Instead the `*` only belongs to `ip`. – too honest for this site Sep 23 '15 at 14:03
  • I see (what I expected). But it also had been possible, that free had a built-in is-stack test (I'm happy to have stopped using `free` when changing from C to C++). Thanks for the clarification! – Wolf Sep 23 '15 at 14:04

4 Answers4

4

First, this line is allocates foo on the stack and then copies dynamically allocated struct to the place:

OBJ foo = *Construct(10, 20);

Of cause, you shouldn't free it. Correct case is:

OBJ* foo = Construct(10, 20);
. . .
freeup(foo);

Second, if you want to clear pointer in freeup, then change the type of parameter to pointer-to-pointer:

void freeup(OBJ** x)
{
   free(*x);
   *x=NULL;
}

...In this case you should change the freeup call into freeup(&foo);.

Finally, it may seem that foo is reallocated in you code, but it just can't be freed or allocated, it's a stack variable. So pointer to foo is always the same inside the function.

Wolf
  • 9,679
  • 7
  • 62
  • 108
Mark Shevchenko
  • 7,937
  • 1
  • 25
  • 29
  • You are double-freeing the allocated memory, which is a problem. You are also passing the value of the pointer twice (you have set a pass-by-value copy of &foo to 0, this does not change &foo). – Vatine Sep 23 '15 at 13:44
  • 1
    Right. ...but it doesn't answer the original question. Maybe suggest a `OBJ **x` parameter as well. (or better not?) – Wolf Sep 23 '15 at 13:44
  • @Wolf Is my comment clearer now? I completely forgot that pressing the return key doesn't give me a new line and instead submitted the comment. :/ – Vatine Sep 23 '15 at 13:46
  • 1
    ... and call `freeup(OBJ **)` like this `freeup(&foo);`. – alk Sep 23 '15 at 13:46
  • @alk I added a line to complete this *option* – Wolf Sep 23 '15 at 13:59
2

You are losing your pointer in the line

OBJ foo = *Construct(10, 20);

You take the struct OBJ that the pointer returned by Construct points to, copy its values to foo and then throw away the pointer. After that, because you have no pointer, you have no way to free the actual struct OBJ allocated by Constructor.

Your freeup calls are wrong then, because you try to free foo, rather than the original.

The correct way is to either keep working with the pointer, so

OBJ *foo = Construct(10, 20);
...
freeup(foo);

or not use pointers at all and just return an OBJ in Construct, rather than an OBJ*. That way, there is no need to worry about freeing anything.

MicroVirus
  • 5,324
  • 2
  • 28
  • 53
1
 OBJ foo = *Construct(10, 20);

Due to this you looses your pointer which points to block allocated by malloc . So freeing memory not allocated by malloc or similar cause error in program.

And this I really don't understand (taking in general - should not be done with pointer also )-

freeup(&foo);              // just one call is enough 
freeup(&foo);              // Why twice ?  

And this -

  void freeup(OBJ *x)
{
    printf("x pointer -> %d ", x);
    free(x);
    x=NULL;
    printf("\nx pointer -> %d\n", x);          // should not be done in any case after assigning pointer to NULL
}

You can do this instead-

  void freeup(OBJ **x)
   {
        printf("x pointer -> %d ", *x);
        free(*x);
       *x = NULL;
  }

Call it in a same manner as you do freeup(&foo);

ameyCU
  • 16,489
  • 2
  • 26
  • 41
1

You have multiple problems in your code:

  1. Losing reference to the original malloc'd pointer
  2. Trying to free an object on the stack
  3. Assigning 0 to the copy of the pointer (passed to freeup)

(1) The line OBJ foo = *Construct(10, 20); doen't assign the pointer to the allocated OBJ to foo, but copies the OBJ's value to the stack, to a variable named foo, this causes you to lose the reference to the allocated object (and cause a memory leak).

A better way to do it is to return the pointer itself, it's clearer, doesn't cause you to lose the reference, and more efficient:

OBJ *foo = Construct(10, 20);

Now foo is a pointer to the object, not the object itself.

(2) derived from (1), now that we use foo as a pointer to the malloc'd object, we can use free on it.

(3) Assigning to a parameter from the callee does not change the value in the caller site. This is known as pass-by-value, as only a copy of the value of the argument is passed to the callee. So let's change freeup:

void freeup(OBJ **x)
{
   printf("x pointer -> %d ", *x);
   free(*x);
   *x=NULL;
   printf("\nx pointer -> %d\n", *x);
}

This way, the value of the pointer at the caller site will be changed.


Note that I omitted NULL checks etc. to make the code clearer, but they should definitely be there.

MByD
  • 135,866
  • 28
  • 264
  • 277