-2

I'm using VScode to practice C and i'm trying to find what exactly does the free() function do to the pointer and the value inside that pointer. Here is my test code

#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <stdlib.h>


int main(void)
{
 int *n = malloc(sizeof(int));
 *n = 1;
 printf("%p %i\n",n, *n);
 free (n);
 printf("%p %i\n",n, *n);


 return 0;
}

when i run the code, here is what i get

0x56405d2e72a0 1
0x56405d2e72a0 1678103271

what i wanted to know is why is the pointer still the same but the value inside have been changed to some kind of garbage value and what does free() explicitly do to the memory ?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 1
    The value of the pointer `n` will not (and can not) change. But once you passed a pointer to `free` you should never dereference it. If you do, then you will have *undefined behavior*. – Some programmer dude Sep 11 '22 at 16:39
  • As far as what the new garbage value means, the allocator often stores freed memory in a linked list like way and hides allocator metadata in the freed memory itself so it can find it again for future allocations. In hex, `1678103271` is `0x6405d2e7`, which you may recognize as a segment of the memory address itself (no, I have no idea why it matches a segment of the memory address, but I trust the allocator is doing something useful with that information, probably in tandem with additional data located to either side of where the pointer is pointing). – ShadowRanger Sep 11 '22 at 16:45
  • 1
    See also [this classic old answer about hotel rooms](https://stackoverflow.com/questions/6441218). (That answer is talking about the storage for local variables in no-longer-active functions, but it applies equally to malloced-and-then-freed storage.) – Steve Summit Sep 11 '22 at 16:51
  • 1
    No function can ever directly change the value of a variable in its caller. That is, if you call `f(x)`, for any function `f()` and variable `x`, `x` is *always* going to have the same value before and after the call to function `f`. That's why when you print `n` before and after calling `free()`, it has the same value. – Steve Summit Sep 11 '22 at 16:57
  • Now, it's worth noting that `n` has the same *numeric* value. But before the call to `free`, it's a valid pointer, while after the call, it's an invalid pointer. Sort of like the way if a judge tells me I'm not allowed to drive any more, that doesn't magically reach into my wallet and make a big red X on my driver's license. But in a database of driver's license information somewhere, now it says "invalid" in the record associated with my number. – Steve Summit Sep 11 '22 at 16:57
  • @SteveSummit: Re “Now, it's worth noting that `n` has the same numeric value”: Like the freed memory that `n` points to, the compiler may treat the memory that holds that value of `n` as freed. Per the C standard, when memory is freed, any pointer to it no longer has a determined value. The optimizer is free to reuse that memory for other purposes, until a new value is assigned to `n`. This means that printing `n` could indeed show a changed value. – Eric Postpischil Sep 11 '22 at 17:56
  • @EricPostpischil Sorry, and I know you don't accept pedagogic simplifications that aren't strictly accurate, and 99% of the time I agree with you, but my therapist has advised me :-) that the best way to move past the trauma of discussing this particular question to death on comp.lang.c lo these many years ago was to put it behind me and move on, so I decided to go with the simplification. For someone who doesn't understand why `free()` doesn't change the pointer, I think it would be likely to confuse them *more* to explain the obscure but completely different reason that it might have. – Steve Summit Sep 11 '22 at 18:31
  • @SteveSummit: It is not an academic question. When somebody tries to debug a program by asking the debugger to show the value of the pointer they just passed to free, to check if they freed what they intended to, or insert a `printf` statement to show the value, they may get a different value than was freed. It will not help the person to have had a simple lesson that taught them the wrong thing than to have a complicated lesson that is correct, even if it is confusing. People need to know the truth. – Eric Postpischil Sep 11 '22 at 18:39

4 Answers4

2

free() doesn't change the pointer - it only allows memory pointed by the pointer to be used by other processes. This means that there won't be any changes until another process says "I want to store some value in 0x56405d2e72a0". The reason why that is good is because now you're allowing other apps and programs to use more space. That's also why using a pointer after it's been freed is considered undefined behavior (because that memory could be changed at any moment)

George
  • 320
  • 3
  • 14
  • "there won't be any changes" - this isn't *entirely* true. The C standard does not require either changing or preserving memory after `free()` but it doesn't prohibit it either. But you can't know, because accessing the memory after `free()` is undefined behavior. – sj95126 Sep 11 '22 at 16:50
  • 1
    Typically, `free` releases the freed memory to be available to be allocated by future calls to `malloc` in *the same* application. Typically, the memory does not become available to *other* apps or processes. – Steve Summit Sep 11 '22 at 17:01
  • 1
    Per the C standard, after `free(x)`, the pointer `x` (not just the memory it points to) does not have a valid value and may be changed by the C implementation. In practice what this means is that the optimizer in the compiler may recognize that a register it is using to hold `x` can be reused for other purposes. If the program attempts to use `x` before assigning it some other value, this can appear as if `x` were changed by `free`. – Eric Postpischil Sep 11 '22 at 17:12
  • `free` generally does not make memory available for other processes to use. Typical behavior for many allocations is that the memory is simple entered into bookkeeping records inside the process that keep track of what memory is available to be allocated by the process, so that it may be later used by a new `malloc` or other allocation. It is possible that if a large amount of memory is freed, the memory allocation software might return it to the operating system for other uses. – Eric Postpischil Sep 11 '22 at 17:15
1

From the C Standard you can read:

The free function causes the space pointed to by ptr to be deallocated, that is, made available for further allocation. If ptr is a null pointer, no action occurs. Otherwise, if the argument does not match a pointer earlier returned by a memory management function, or if the space has been deallocated by a call to free or realloc, the behavior is undefined.

mikyll98
  • 1,195
  • 3
  • 8
  • 29
  • 1
    `free` generally does not make memory available for other processes to use. Typical behavior for many allocations is that the memory is simple entered into bookkeeping records inside the process that keep track of what memory is available to be allocated by the process, so that it may be later used by a new `malloc` or other allocation. It is possible that if a large amount of memory is freed, the memory allocation software might return it to the operating system for other uses. – Eric Postpischil Sep 11 '22 at 17:15
  • @EricPostpischil so what *actually* happens is that the memory is still "property of" the process who freed? – mikyll98 Sep 11 '22 at 17:28
  • 1
    Yes, commonly. Returning pages to the operating system only to have them reallocated again would waste time, so it is not something you want a process doing frequently. – Eric Postpischil Sep 11 '22 at 17:31
  • @EricPostpischil thank you for the clarifications, I'll have to revise my Operating Systems course notes :) – mikyll98 Sep 11 '22 at 17:33
0

For a beginner, the mysteries of malloc() and free() are best just trusted if they are respected and used appropriately.

I just ran this

#include <stdio.h>
#include <stdlib.h> // for 'exit()'

int main() {
    char *p;

    p = malloc( sizeof *p );
    if( p == NULL ){
        fprintf( stderr, "malloc failed\n" );
        exit( EXIT_FAILURE );
    }
    printf( "%p\n", p );
    free( p );

    p = malloc( sizeof *p );
    if( p == NULL ){
        fprintf( stderr, "malloc failed\n" );
        exit( EXIT_FAILURE );
    }
    printf( "%p\n", p );
    free( p );

    return 0;
}

Output

00881420
00881450

Notice that malloc() chose to issue a different block of memory in the second call even though there was no apparent "heap activity" between one free() and the following malloc().

Notice, too, that the "size" requested is defined by the size that the awaiting pointer should point at. (Also, fewer parentheses!) The declaration of 'p' could be changed to int, long or double or even a large, complex struct with only a single line changed in the code above.

Comments on your code - Don't use more #include statements than you need in any given source file, and "watch the whitespace". When source files become more complex, you will want to already have good style habits.

Finally, Always check the "return codes" from library functions. Always.

Fe2O3
  • 6,077
  • 2
  • 4
  • 20
0

Calling free() makes memory previously allocated (by malloc(), realloc() or calloc() available for reallocation.

Behind the scenes free() will return the memory to whatever container is being used to record the available memory.

It may return memory to the Operating System for reallocation to other processes but a typical call just records it as available for reallocation.

Internally it may combine adjacent free areas into larger areas that can be split or allocated again later.

If you imagine there's a linked list of 'free' variable sized pieces of free memory and malloc() returns one of those or restructures one into a piece of suitable size and some spare then think of free handed that back and linking it in.

Persixty
  • 8,165
  • 2
  • 13
  • 35