14

This assignment asks us to allocate two int-type variables using malloc() (named var1 and var2), print the addresses of each variable (the address of the pointer on the stack and the address on the heap), then use free() to deallocate var1, print the addresses again, then allocate another space in the heap for var1 and print the addresses a third time. I believe that the instructor is trying to show us that the heap address for var1 is supposed to change, but it always stays the same... unless I remove free(var1) from the code. The instructor did a similar demonstration, but did not use free() to deallocate any variables, so we never saw how this was supposed to work.

Here is my code:

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

void main()
{

int *var1 = (int*)malloc(sizeof(int)); 
*var1 = 1000;                   
int *var2 = (int*)malloc(sizeof(int)); 
*var2 = 2000;

printf("Addresses of var1\n");
printf("Pointer on stack: %p / Heap: %p\n\n", &var1, var1); 
printf("Addresses of var2\n");
printf("Pointer on stack: %p / Heap: %p\n\n", &var2, var2);

free(var1);

printf("AFTER DEALLOCATING var1 FROM THE HEAP\n");
printf("Addresses of var1\n");
printf("Pointer on stack: %p / Heap: %p\n\n", &var1, var1); 
printf("Addresses of var2\n");
printf("Pointer on stack: %p / Heap: %p\n\n", &var2, var2);

var1 = (int*) malloc(sizeof(int));
*var1 = 1500;

printf("NEW MEMORY ADDRESS ALLOCATED FOR var1\n");
printf("Addresses of var1\n");
printf("Pointer on stack: %p / Heap: %p\n\n", &var1, var1); 
printf("Addresses of var2\n");
printf("Pointer on stack: %p / Heap: %p\n\n", &var2, var2); 

}

This code results in this output:

Addresses of var1
Pointer on stack: 0xffffcbf8 / Heap: 0x600000390

Addresses of var2
Pointer on stack: 0xffffcbf0 / Heap: 0x6000003b0

AFTER DEALLOCATING var1 FROM THE HEAP
Addresses of var1
Pointer on stack: 0xffffcbf8 / Heap: 0x600000390

Addresses of var2
Pointer on stack: 0xffffcbf0 / Heap: 0x6000003b0

NEW MEMORY ADDRESS ALLOCATED FOR var1
Addresses of var1
Pointer on stack: 0xffffcbf8 / Heap: 0x600000390

Addresses of var2
Pointer on stack: 0xffffcbf0 / Heap: 0x6000003b0

As you can see, the heap address does not change for var1 when I deallocate it, and it doesn't change when I allocate memory space for var1 again. However, if I simply remove the free(var1) line from the program, it simply assigns a second memory space for var1 and points to that on the heap, which DOES have a different memory address:

Addresses of var1 
Pointer on stack: 0xffffcbf8 / Heap: 0x600000390

Addresses of var2
Pointer on stack: 0xffffcbf0 / Heap: 0x6000003b0

AFTER DEALLOCATING var1 FROM THE HEAP
Addresses of var1
Pointer on stack: 0xffffcbf8 / Heap: 0x600000390

Addresses of var2
Pointer on stack: 0xffffcbf0 / Heap: 0x6000003b0

NEW MEMORY ADDRESS ALLOCATED FOR var1
Addresses of var1
Pointer on stack: 0xffffcbf8 / Heap: 0x600000420

Addresses of var2
Pointer on stack: 0xffffcbf0 / Heap: 0x6000003b0

(Just to be clear, all I did was remove free(var1) from the previous code, so the "AFTER DEALLOCATING var1" section now shows the exact same heap address as the previous set, but it DOES change the heap address of var1 in the third section.)

Can anybody tell me what is happening here? The only logical explanation I can come up with is that when I'm using free() to deallocate var1 and then printing the address, it's simply printing the LAST address that it pointed to, and then when I'm allocating memory for var1 the second time, it's simply "backfilling" the previous address with the new value of var1. Does this make sense? Do I have errors in my code, or is this just how C behaves when deallocating memory for a variable and then reallocating it?

Mat
  • 188,820
  • 38
  • 367
  • 383
Dustin R.
  • 143
  • 6
  • 2
    1. `free()` takes a pointer which you might provide with a variable. However, it doesn't change the contents of that variable. (Pointer is given by value to `free()`.) 2. If you `free()` N bytes and later (rather immediately) `malloc()` N bytes again, why shouldn't you get the same address? Sounds, like heap management does a good job - it is re-using the free space of sufficient size. ;-) – Scheff's Cat Oct 23 '18 at 14:16
  • 3
    It makes perfect sense for a `malloc` implementation to reuse space for a new object of the same size. – John Bode Oct 23 '18 at 14:16
  • Hey there. Not what you asking, but you don't need to [cast the return value of malloc](https://stackoverflow.com/questions/32652213/why-does-the-book-say-i-must-cast-malloc/32652435) anymore. Also, I find useful to use `int *var = malloc(sizeof *var)` instead of `int *var = malloc(sizeof(int))`. That way I can change the type of `var` without the risk of forgetting to change it inside the malloc call (e.g. `float *var = malloc(sizeof(int))`). – ddz Oct 23 '18 at 14:27
  • 1
    You're basically right. The lesson here is that, even after freeing memory, you may still have variables pointing to that memory, despite the fact that you don't own it. These "dangling pointers" can cause lots of misery, and that's why it's considered good practice to clear such pointers, e.g. `free(p);p=NULL;` – Tim Randall Oct 23 '18 at 16:30

3 Answers3

11

It is perfectly normal that malloc might return the same addresses when memory is freed and then reallocated. It would also be normal for it to return different addresses.

If you change the malloc calls to request different sizes than the original allocations, you might get different addresses, since the old blocks malloc had prepared might not be enough for the new requests. But they might be enough, so the addresses might not change.

Incidentally:

  • void main() is incorrect. It should be int main(void).
  • Printing an address after the space it points to has been freed is not supported by the C standard. It is not uncommon for it to “work,” but it is not proper. C 2018 6.2.4 2 tells us “The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime.” When an object allocated with malloc is freed with free, its lifetime ends.
Eric Postpischil
  • 141,624
  • 10
  • 138
  • 247
  • _Printing an address after the space it points to has been freed_... Can't you print any address (as long as you don't access contents if it's not a valid address)? – Scheff's Cat Oct 23 '18 at 14:21
  • @Scheff: No. The C standard does not require pointers to be simply numeric addresses in a flat address space. Some C implementations use complicated addressing schemes (e.g,, segment and offset, and there are others), and these might require part of a pointer to be “active” in that it refers to some other address or register or structure that is dynamically set up. When space is freed, that other information may be deconstructed. Then, when `printf` attempts to format the pointer for `%p`, its attempt to use that information may fail. – Eric Postpischil Oct 23 '18 at 14:26
  • Thanks. I find this is an interesting point worth to be mentioned in your answer. It's a bit beyond "it's just a value that can be printed" and interesting for whom which are used to flat addresses without harm. (Segment/offset addressing reminds me to the ancient DOS on 8086 but even in that case, I cannot remember that printing an invalid address was anyhow dangerous.) – Scheff's Cat Oct 23 '18 at 14:31
5

The only logical explanation I can come up with is that when I'm using free() to deallocate var1 and then printing the address, it's simply printing the LAST address that it pointed to

Kind of right. Freeing a pointer does not affect the content of the pointer at all. Or to be more precise, it's value is indeterminate after being freed. In fact, according to the standard you cannot even trust the pointer to contain the address it did before invoking free. As far as I know, it will in most cases, but you cannot trust it.

The standard says:

C 2018 6.2.4 2: “The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime.”

A common mistake is to use the test if(ptr == NULL) as a check if you have properly freed the pointer. This will not work.

klutt
  • 25,535
  • 14
  • 43
  • 72
  • 3
    The C standard does not guarantee that “Freeing a pointer does not affect the content of the pointer at all.” After a pointer is passed to `free`, using its value has unspecified behavior. – Eric Postpischil Oct 23 '18 at 14:18
  • @EricPostpischil but how can `free(pointer)` change the value of `pointer`? Sure after freeing, that value is meaningless, but the bit pattern of `pointer` will remain. After all `free` is just a function call. But, yes, I can't think of a use case where that meaningless value could be used for anything useful. – Jabberwocky Oct 23 '18 at 14:25
  • @EricPostpischil `free` can't change the value of the pointer passed to it though. So it will remain the same value as it was before, previously valid and with no trap representations. The contents of that address can of course no longer be accessed, but that's another story. This appears to be yet another error in informative Annex J of the standard. There is no normative text saying that the pointer takes an indeterminate value or some such. – Lundin Oct 23 '18 at 14:28
  • @Jabberwocky: `free` cannot change the bytes that represent `pointer`, but it can change data structures that are used to organize memory and addressing. See my comment on my answer. – Eric Postpischil Oct 23 '18 at 14:29
  • @EricPostpischil Ah yeah you are correct, I read that wrong. You should add the quote to your answer. – Lundin Oct 23 '18 at 14:34
  • I added the quote to this answer, but I guess it does not harm to add it to Erics answer too. – klutt Oct 23 '18 at 14:34
  • `free()` cannot fail so you cannot check if it failed, the common mistake is actually trying to check at all if it failed, because it cannot fail. If it fails, you'll see it with it trashing your hard drive or something related to that. calling free on a valid pointer will always work, calling free on NULL will always do nothing, calling free on invalid pointer will be undefined behaviour, this is why it's advised to free like this: `free(ptr); *ptr = NULL;` Now it's impossible that you'll corrupt anything even if you `free()` it again for no reason whatsoever, whether it's mistake or not. – Purple Ice Oct 24 '18 at 16:39
  • Unless you'll assign something else to `*ptr` right away, it should always be NULL, so if you try to dereference it, it will crash instead of corrupting your data. – Purple Ice Oct 24 '18 at 16:42
4

In the first example, the value of where the pointer used to point at the heap remains untouched at the stack after free(). But you can no longer access that address.

When they allocate the variable yet again, it is not strange that you get the same address, as there should be an available segment of the right size at that address. You are however not guaranteed to get the same one - which address that's the best pick from what's available is handled by the library code.

Lundin
  • 155,020
  • 33
  • 213
  • 341