2

I was trying to explain how free works to a friend, so I thought up the following code example to try and explain it (Complex, I know):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{

  char *stringPtr = NULL;

  //Pass the size of the string in bytes to malloc
  stringPtr = (char *)malloc(10 * sizeof(char));

  strcpy(stringPtr, "A String. ");
  printf("%s", stringPtr);
  free(stringPtr);

  printf("%s", stringPtr);

  return 0;
}

Thinking that this code would free up the memory allocated on the heap, I assumed that if I then went to print out the string that was assigned to the pointer, it would then do something weird or crash the program since I assumed it would try to output a null value; however when I ran it, it ended up just outputting the string twice. I figured I'd step through it to see why, because in my mind I thought maybe it copied the string to the stack or something and the pointer would then be pointing to a new memory address and the old memory got cleaned up after all (not that that scenario would make any sense, but hey, I didn't know what else it would be), but when I stepped through the code not only did it not free up the memory, the pointer actually was still pointing to the same memory address and it had that same value.

As I looked into it people said that you need to assign the pointer a null value after calling free, but my question is why? Shouldn't free just deallocate that memory and set the pointer to null? What's the point in preserving the value? Or am I just not understanding something?

Trevor Hart
  • 993
  • 7
  • 26
  • And yes looking into it reassigning to NULL does fix this, but I'm still curious why. – Trevor Hart Nov 21 '15 at 06:14
  • De/unallocated memory is not the same as zeroed memory. Compare malloc and calloc: the first allocates memory (opposite of free), the second one also zeroes it (there is no free equivelent for that, because it's normally not necessary). –  Nov 21 '15 at 06:23
  • 1
    `strcpy(stringPtr, "A String. ");` need `stringPtr = (char *)malloc(11 * sizeof(char));` – BLUEPIXY Nov 21 '15 at 06:24
  • Oh shoot, you're right hahaha, I accidentally put a space at the end, good thing C doesn't do any checking on that stuff... – Trevor Hart Nov 21 '15 at 06:25
  • @BLUEPIXY `sizeof(char)` is always 1, so no need to use IR. – Sami Kuhmonen Nov 21 '15 at 07:03
  • @SamiKuhmonen I know it. but thanks. The cast also not necessary in C. ;-) – BLUEPIXY Nov 21 '15 at 07:10
  • Actually it is needed when I compile, it complains that it can't cast from void* to char* if I don't cast it – Trevor Hart Nov 21 '15 at 08:49
  • That is because you are using C++ instead of C. – BLUEPIXY Nov 21 '15 at 09:31
  • @SamiKuhmonen It is better to spend that extra 3 seconds to type it out, because it will make your code self documenting/show the intent behind the allocation. – this Nov 21 '15 at 10:45
  • @this So, it will say "I want to allocate n chars" instead of "I want to allocate n chars"? Since without it it is also obvious what you wanted and even more obvious what the amount allocated is. – Sami Kuhmonen Nov 21 '15 at 10:47
  • @SamiKuhmonen That is an argument from personal incredulity. I suggest you try to find a counterexample for your argument. It shouldn't be too hard. I would present one, but that would defeat the exercise. – this Nov 21 '15 at 10:55

4 Answers4

9

Shouldn't free ... deallocate that memory

Suppose your company decides to move your workspace, so they allocate you a new desk and deallocate your existing one. Are they going to clear out your old desk immediately? Probably not, unless they need it immediately. If you go back and check the drawers, you might find that gum you left there, or you might not. Your space was deallocated, but that doesn't mean it's immediately cleared or gone. It's just not your space any more.

Shouldn't free ... set the pointer to null?

free gets a copy of your pointer, since the pointer is passed by value. free can't wipe the variable you were holding the pointer in.

user2357112
  • 260,549
  • 28
  • 431
  • 505
2

After a successful malloc, the calling process takes the ownership of the memory area returned by malloc and this process can store and read from this memory space.

When the process releases it by calling free(), the process informs that it is done with the memory and releasing it back to the system. Now the system can allocate the same piece of memory to other process immediately or at a later point in time. The system owns the memory. It does not clear it up. If the memory is allocated again to a different process, it can use it to store its own data.

Please note that process is not supposed to access this memory can end up in segmentation fault if it writes to that memory. The process should not read or write the memory anymore.

The variable holding the memory address is owned by the process and call to free the memory is actually 'call by value' (the value of the pointer variable is passed), so the free function cannot clear the pointer variable.

In some cases it is usually a good practice to set the pointer variable to NULL after freeing the memory because it helps in avoiding/identifying the cases of using freed memory.

Arun Kaushal
  • 593
  • 3
  • 16
0

The free function obtains the pointer value, not the pointer variable so it is unable to set a new value to it. This is how the memory allocation API has been designed and we have to stay with it.

dlask
  • 8,776
  • 1
  • 26
  • 30
0

I had this same question. I proved to myself that you should definitely not use the pointer after its freed and does give undefined behavior. Sometimes it works, other times it does not work, and will crash in a strange way. Example is below.

#include <iostream>
#include <malloc.h>

int main()
{
    int n = 100000;
    float* testfree = (float*)malloc(sizeof(float) * n);
    printf("test pointer address in direct free: %p\n", testfree);

    for (int i = 0; i < n; i++)
        testfree[i] = i * i;

    int ind = 6;
    printf("testfree[%d] = %f\n", ind, testfree[ind]);

    system("pause");

    free(testfree);

    printf("test pointer address after free: %p\n", testfree);
    printf("testfree[%d] = %f\n", ind, testfree[ind]);

    return 42;
}

OUTPUT:

test pointer address in direct free: 000001D374F3E150
testfree[6] = 36.000000
Press any key to continue . . .
test pointer address after free: 000001D374F3E150
testfree[6] = 36.000000

C:\Users\ToshibaUser\source\repos\mallocfreereturn\x64\Release\mallocfreereturn.exe 
(process 19652) exited with code 42.
Press any key to close this window . . .

This works every time for me, prints values and pointer address it had before the free().

When n = 1,000,000 or higher it crashes (I only tried multiples of 10, cant go higher than max for 32 bit integers also...) but, the crash point is confusing because, in my debugger in MSVS, the error location is at the free(), if you comment out only this line

//printf("testfree[%d] = %f\n", ind, testfree[ind]);

it will not crash and will still get the right pointer address with really large values of n (at least 1,000,000).

Next thing I checked is freeing the pointer/block after it was allocated in a function with only the pointer returned. I did confirm it does exactly the same thing. malloc() only returns the pointer weather its in a different function or used directly, and its up to you to figure out what to do with defining what is in the block and remembering how big that block is. So this is not really any different. Actually, the memory chunk does contain extra info that is not visible to the developer, see references for deep dive. I had to make sure the memory is actually freed and doesn't take up RAM, because my application completely fills my RAM, and I do some calculations then I free some stuff, then I allocate different stuff. It's not that I have a lame computer, I have 128 GB of RAM, I'm doing scientific computing :)

Definitely the free() does free up the RAM but the memory still has the values you gave it because why clean your room until someones coming over?

THIS HAS EXACTLY THE SAME RESULTS:

#include <iostream>
#include <malloc.h>

float* mallocret(int n);

int main()
{
    int n = 10000000000;
    float* freeme = mallocret(n);
    printf("returned pointer address outside function: %p\n", freeme);

    int ind = 6;
    printf("freeme[%d] = %f\n", ind, freeme[ind]);

    system("pause");

    free(freeme);

    printf("returned pointer address outside function: %p\n", freeme);
    printf("freeme[%d] = %f\n", ind, freeme[ind]);

    return 42;
}

USING THIS FUNCTION DEFINED IN A SEPARATE FILE

#include <iostream>
#include <malloc.h>

float* mallocret(int n)
{
    float* retp = (float*)malloc(sizeof(float) * n);
    printf("return pointer address in function: %p\n", retp);

    for (int i = 0; i < n; i++)
        retp[i] = i*i;

    return retp;
}

OUTPUT:

return pointer address in function: 00000297C1B4DF70 returned pointer address outside function: 00000297C1B4DF70 freeme[6] = 36.000000 Press any key to continue . . . returned pointer address outside function: 00000297C1B4DF70 freeme[6] = 36.000000

C:\Users\ToshibaUser\source\repos\mallocfreereturn\x64\Release\mallocfreereturn.exe (process 8032) exited with code 42. Press any key to close this window . . .

DONT USE THE POINTER AFTER ITS FREED! IT DOES NOT ALWAYS WORK!

note: always return 42 from main.

REFERENCES:

How does free know how much to free?

How do free and malloc work in C?

https://www.gnu.org/software/libc/manual/html_node/Freeing-after-Malloc.html

https://www.interviewsansar.com/how-does-free-know-amount-of-memory-to-deallocate/

https://www.geeksforgeeks.org/g-fact-88/

https://en.wikipedia.org/wiki/C_dynamic_memory_allocation

https://en.wikipedia.org/wiki/Cruft