2

All of the stuff I could find referring to freeing 'double' pointers refers to freeing pointers to pointers, which is not what I am talking about. I already figured out how to do that.

It might not even be a big thing at all, but I noticed that when I free() an int, char, or float pointer, the value is removed or replaced with a garbage value, but when I free() a double pointer, the value remains the same.

Here's the code I tested this with:

int main()
{
  int *p_a = malloc(sizeof(int));
  *p_a = 1;

  char *p_b = malloc(sizeof(char));
  *p_b = 'b';

  float *p_c = malloc(sizeof(float));
  *p_c = 3.565;

  double *p_d = malloc(sizeof(double));
  *p_d = 7.77;

  printf("Before 'free()':\n");
  printf("A: %p\t%d\n", p_a, *p_a);
  printf("B: %p\t%c\n", p_b, *p_b);
  printf("C: %p\t%g\n", p_c, *p_c);
  printf("D: %p\t%g\n", p_d, *p_d);

  free(p_a);
  free(p_b);
  free(p_c);
  free(p_d);

  printf("\nAfter 'free()':\n");
  printf("A: %p\t%d\n", p_a, *p_a);
  printf("B: %p\t%c\n", p_b, *p_b);
  printf("C: %p\t%g\n", p_c, *p_c);
  printf("D: %p\t%g\n", p_d, *p_d);

  return 0;
}

This produced the following output:

Before 'free()':
A: 0x8f2e008    1
B: 0x8f2e018    b
C: 0x8f2e028    3.565
D: 0x8f2e038    7.77

After 'free()':
A: 0x8f2e008    0
B: 0x8f2e018
C: 0x8f2e028    1.46175e-33
D: 0x8f2e038    7.77

Is that normal? My understanding of how malloc() and free() work is that when you malloc() a pointer, you set aside some bytes of memory for the pointer to point to. This is put on the heap, where it cannot be accessed, and it will not come off the heap until it is free()d or a memory clean-up is triggered on the computer, which is not controlled by the user. So, when you free() a pointer, you basically get rid of the association between the pointer and the address it points to, allowing that memory to be used later on.

Given all that, why is the double pointer still pointing to the same value?

Now that I think about it, why do the pointers have an address at all after the free()s?

As I am a beginner to C, any and all wisdom and knowledge is much appreciated. Thank you for your time.

EDIT: After further testing and meddling, I found that if you enter a number that has more than 6 digits and rounds up on the last one (eg. 777.7777) for the double, after it is free()d it loses its rounding.

To illustrate:

Before 'free()':
D: 0x8f2e038    777.778

After 'free()':
D: 0x8f2e038    777.777

This supports @rodrigo's hypothesis (in the comments) that, because the double is 8 bytes - as opposed to int, char and float's 4 bytes - free() only modifies the first 4 bytes, so it does not lose all of its data.

Thanks everyone for your help!

Jasper
  • 300
  • 2
  • 11
  • 6
    It's simple undefined behaviour. – Kerrek SB May 05 '14 at 18:14
  • I didn't see any double pointers in your code. – gangadhars May 05 '14 at 18:15
  • `double *p_d = malloc(sizeof(double));` is a pointer to a `double` value. – Jasper May 05 '14 at 18:16
  • @SGG: double pointer or `double` pointer :-) – Kerrek SB May 05 '14 at 18:16
  • http://stackoverflow.com/a/6445794 ... C++ specific, but applies equally well here. – Robert Harvey May 05 '14 at 18:16
  • Sorry OP. I thought double pointer means `**` – gangadhars May 05 '14 at 18:16
  • @KerrekSB: That question does not appear to address this specific one. – Robert Harvey May 05 '14 at 18:18
  • No problem, I tried to clarify that early on in the post. – Jasper May 05 '14 at 18:18
  • @RobertHarvey So basically, after you've `free()`d the pointers, the program just goes "Well, here's a value from _somewhere_..."? – Jasper May 05 '14 at 18:19
  • 4
    Yes. C is what we call an "unsafe" language. It makes no guarantees about what you will find if you try and access memory that is no longer claimed by you. All `free()` is required to do is tell the OS that the memory is available to be reused for something else. If you try and access memory after you `free()` it, that's not anyone's problem but yours. – Robert Harvey May 05 '14 at 18:21
  • @RobertHarvey Well, alrighty then. I understand that part. But I'm still a little confused as to why the `double` retained its value. – Jasper May 05 '14 at 18:24
  • Probably because that's what was there before you called `free()`. – Robert Harvey May 05 '14 at 18:25
  • It just seems strange. The others output garbage values, different each time, but the `double` always outputs the same before and after. I mean, I guess it doesn't matter in the end, as long as the memory is free, but I just like to know why things happen all the time. :P – Jasper May 05 '14 at 18:27
  • 1
    Anyways, there is a sort of comfort in the knowledge that it literally doesn't matter what happens after `free()`. I can rest easy knowing that those young, adventurous bytes have a full life of randomness ahead of them. Maybe the `double` prefers a more sedentary lifestyle. :P – Jasper May 05 '14 at 18:31
  • If you want to know what `free` does when you free a pointer to double, **obtain the source code** and then you'll know. Anything else is speculation. – Eric Lippert May 05 '14 at 21:21

2 Answers2

7

My understanding of how malloc() and free() work is that when you malloc() a pointer, you set aside some bytes of memory for the pointer to point to.

You don't malloc a pointer. You malloc a block of memory. The value returned by malloc is a pointer to that memory. So let's rephrase that.

My understanding of how malloc() and free() work is that when you malloc() a block of memory, you set aside some bytes of memory; the pointer returned points to the start of that block of memory.

Correct.

This [the memory block] is put on the heap

Correct; think of the heap as long-term random-access storage.

where it cannot be accessed

This is either wrong or nonsensical. Where what cannot be accessed, and by whom?

it [the block of memory] will not come off the heap until it is free()d

The memory block allocated by malloc is reserved for the use of your program until free is called. To free the block, you call free and pass the pointer that was originally returned from malloc, yes.

or a memory clean-up is triggered on the computer, which is not controlled by the user

This is again incoherent. Modern operating systems use virtual memory. When a process is destroyed the entire virtual address space vanishes.

when you free() a pointer, you basically get rid of the association between the pointer and the address it points to, allowing that memory to be used later on.

No no no no no. This is the crux of your misunderstanding.

You free a block of memory by passing the pointer that was originally returned by malloc to free. That frees the block of memory, and malloc is then allowed to hand it out again, should other code in your process ask for memory.

But free does NOT change what address the pointer points to! The pointer continues to point to an address; that address is now invalid! Dereferencing a pointer to invalid memory is called "undefined behaviour" in C, and literally anything can happen. Your program can crash. You can get the value that used to be there back. Your password can be emailed to hackers in a secret lair at the north pole. Anything can happen, so don't do that.

(Coincidentally I have earlier today written a blog article about this subject which will go up on Wednesday, so watch my blog for details.)

Given all that, why is the double pointer still pointing to the same value?

Undefined behaviour means that anything can happen. The pointer still pointing to the same value is something happening, and something happening is a subset of anything happening.

Now that I think about it, why do the pointers have an address at all after the free()s?

The pointer is stored in a variable. You didn't change the value of the variable. Variables keep their values until you change them.

Where can I read more?

This highly related question might be of interest to you.

Can a local variable's memory be accessed outside its scope?

That question is the same as yours, except in the given question the pointer is to the stack, not to the heap. But the logic is the same: dereferencing a pointer to a popped stack frame is undefined behaviour, just as dereferencing a pointer to a free'd block is undefined behaviour. Anything can happen.

Community
  • 1
  • 1
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • Thank you. This was very informative. I'm sure I now know far more about `malloc()` and `free()` than my current coursework requires me to. Certainly not a bad thing, in my book. :P – Jasper May 05 '14 at 23:25
  • About the `where it cannot be accessed` bit: My professor has told us that memory that is on the heap is essentially inaccessible by other programs, and the failure to free up memory after allocating it is the cause of memory leakage, as more and more bytes get allocated over time, and none of them get taken off of the heap. Is this wrong, or just an outdated concept? Is it still true on older systems? – Jasper May 05 '14 at 23:38
  • I did some more research, and I believe I understand now. Memory is freed from the heap after a program finishes running regardless, but for programs that are continually running, memory leak is an issue. Is this correct? If so, my professor needs to do a little more research on the topic, or just better rephrase his lesson on it. – Jasper May 06 '14 at 01:33
  • It's also undefined behaviour to even mention the value of a freed pointer. The hardware could do checks on an address register and trap if it is out of range. – M.M May 06 '14 at 01:54
  • @TheQZ: The fact that addresses in one process have no meaning in another process is true of all memory in a process, not just the heap. Back in the old days, like Windows 3.0 days, every process shared the same memory and badly-behaved processes could corrupt each other's memory. In modern operating systems that use virtual memory, every process has its own virtual memory space, and two processes have to use special techniques to share memory. – Eric Lippert May 06 '14 at 02:45
  • @TheQZ: And yes, if a process keeps allocating *virtual address space* and never releases it then eventually it will run out; it's a finite quantity. – Eric Lippert May 06 '14 at 02:46
  • This has been helpful. I might talk to my professor about it, although he doesn't seem the type to enjoy having a student dispute his knowledge. Either way, thank you all very much. – Jasper May 06 '14 at 02:50
  • @TheQZ: You're welcome. BTW coincidentally I wrote a blog article about some of these topics this morning; it will go up on my blog on Wednesday, so check it out if you're interested. – Eric Lippert May 06 '14 at 02:53
2

Basically, once you free a pointer, you should not use the data it points to anymore.

malloc allocates for you some chunk of memory that you are responsible to write and read. That chunk 'belongs' to you, but it's only a rent!

After you free that chunk, it's back to the system. As said above, using that data is undefined behavior: expected result (as if no free), wrong result, crash...

As per the double issue, try to invert the freeing (D to A) and see what happens... (and anyway, malloc doesn't know you intend to put a double in that space).

Déjà vu
  • 28,223
  • 6
  • 72
  • 100
  • The `double` stays the same, no matter what. I guess it doesn't really matter, given the unpredictability of unallocated memory, but it just seemed strange. – Jasper May 05 '14 at 18:28
  • 1
    @TheQZ: I'm guessing the following explanation for your observations: in this `malloc` implementation, when the memory is freed, the first bytes of the block are overwritten, maybe marking it as free, or linking it in a free list. If 4 bytes are overwritten, `int`, `float` and `char` values will be destroyed, but `double` values, being 8 bytes long are only destroyed in the lest significant half. T hey are changed just little, and since you print only a few decimals, you didn't notice. – rodrigo May 05 '14 at 18:52
  • Thanks, that seems logical enough. Much appreciated! It was kinda bugging me. :P – Jasper May 05 '14 at 19:05