7

I have this struct type that I malloc for, and after I free it the pointer still points to the data I assigned. Is that just because the pointer is pointing to memory that is free but hasn't been reallocated yet?

#include <stdio.h>

struct S {
    int value;
}

int main () {
    S *s = malloc(sizeof(struct S));
    s->value = 8910;
    free(s);
    printf("s: %i\n", s->value);
}
  • 6
    In a word, yes. It's undefined behavior which means you sometimes get lucky and sometimes not so much. – Duck Sep 02 '13 at 23:38

3 Answers3

19

Freed memory doesn't belong to you anymore. But that doesn't mean it disappears or gets changed in any way. Why would your program bother? It would be a waste of time. It probably just marks the memory as available for use by subsequent malloc()s, and that's it. Or it might not. Using memory that doesn't belong to you might do anything: return wrong values, crash, return right values, or run a flight simulator game. It's not yours; don't mess with it and you'll never have to worry about what it might do.

Lee Daniel Crocker
  • 12,927
  • 3
  • 29
  • 55
  • 5
    I wish *my* UB programs would run flight sims. *My* UB programs seem to just give me nasal demons... – nneonneo Sep 02 '13 at 23:43
  • 1
    My guess is that in this particular case, the UB is unleashing a small horde of helpful brownies who then force the `printf()` to print the original value. – verbose Sep 03 '13 at 00:38
4

The C standard defines the behavior of the free function:

The free function causes the space pointed to by ptr to be deallocated, that is, made available for further allocation.

which means that a later call to malloc (or something else) might re-use the same memory space.

As soon as a pointer is passed to free(), the object it pointed to reaches the end of its lifetime. Any attempt to refer to the pointed-to object has undefined behavior (i.e., you're no longer allowed to dereference the pointer).

More than that, the value of the pointer itself becomes indeterminate, so any attempt to refer to the pointer value has undefined behavior. Reference: N1570 6.2.4p2:

If an object is referred to outside of its lifetime, the behavior is undefined. The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime.

It's true that free()'s argument is passed by value (like all C function arguments), and so free can't actually modify the pointer. One way to think of it is that the pointer has the "same" value before and after the call, but that value is valid before the call and indeterminate after the call.

It's likely that an attempt to refer to the pointer value, or even to dereference it, will appear to "work". That's one of the many possible symptoms of undefined behavior (and arguably the worst, since it makes it difficult to detect and diagnose the error).

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • The pointer _itself_ becomes indeterminate? I'm not so sure about that. Is there a reference in the standard that states this? – paxdiablo Sep 03 '13 at 04:02
  • @KeithThompson: Does the fact that the pointer is indeterminate mean that all equality comparisons involving it become undefined behavior or merely have indeterminate results? Certainly if a pointer is freed, a compiler can't be expected to guarantee that the pointer will not in future compare equal to one that points to a valid data structure, but that wouldn't seem to imply nasal demons. There may not be many places where one would perform a comparison and end up not caring about the result, but if `foo` and `bar` are data pointers and `proc` is a function pointer... – supercat Sep 03 '13 at 17:16
  • ...I could imagine something like `if ((foo != NULL) || (bar != NULL)) proc(foo,bar);` if code could guarantee that at any time when either `foo` or `bar` was invalid, `proc` would refer to a function which only used the other (which would be valid). To be sure, having the "if" outside the function call probably wouldn't be overly helpful, but if a common case was for `foo` and `bar` to both be null and the function call would be expensive, the test could improve efficiency. – supercat Sep 03 '13 at 17:19
  • @supercat: Yes, it means that any reference to the pointer's value has undefined behavior. Consider a system where pointer comparisons are done by loading the pointer values into a special CPU register, and that the act of loading the address checks that it's valid. C does not exclude such an implementation. When the pointer becomes indeterminate, it *could* become a trap representation. – Keith Thompson Sep 03 '13 at 18:02
  • @KeithThompson: Would "any reference to the pointer's value" include simple assignments from one variable of a pointer type to another variable of the same type, or does it only include comparisons, arithmetic, and dereferencing operations? – supercat Sep 03 '13 at 18:16
  • @supercat" Anything that reads the value is a "reference". In practice, on most implementations, most references are going to be harmless; the point is that the C standard does not define the behavior, leaving implementations free to do funky stuff if they choose to. It could also mean that an optimizing compiler can make certain assumptions; given: `free(ptr); if (ptr == something)` the comparison might not even be evaluated. – Keith Thompson Sep 03 '13 at 18:37
  • @KeithThompson: To be a little more specific in my last question, given `void ignore(void *p) {}`, would `free(ptr); ignore(ptr);` invoke Undefined Behavior because `ptr` would be read and copied to `p`, or would the act of reading and copying the pointer not be considered a "reference"? – supercat Sep 03 '13 at 19:02
  • 2
    @supercat: That's a reference, and therefore UB. An expression statement `ptr;` is also a reference. Pretty much any reference to the name other than as an operand of `sizeof` or `&`, or on the LHS of an assignment, reads the value (in the abstract machine) and therefore has undefined behavior. – Keith Thompson Sep 03 '13 at 19:03
  • @KeithThompson: Hmm... that would seem a pretty strong argument in favor of nulling out pointers when freeing them, since copying a null pointer is 100% legitimate defined behavior. Further, I believe that if the pointer were part of a struct, copying the entire struct would be defined behavior. I'm thus not sure in what cases a compiler would gain by having a simple copy operation on a pointer behave differently than one on a struct containing that pointer; if the spec doesn't explicitly specify that "reference" includes a straight copy, that might be a useful clarification in a future rev. – supercat Sep 03 '13 at 19:10
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/36744/discussion-between-keith-thompson-and-supercat) – Keith Thompson Sep 03 '13 at 19:20
  • Hello, Keith. My blog post http://trust-in-soft.com/dangling-pointer-indeterminate/ (password: ntpd) failed to convince and I found this discussion while looking for further arguments. Do you have any remarks about the post? – Pascal Cuoq Jul 04 '14 at 08:29
2

free() just declares, to the language implementation or operating system, that the memory is no longer required. When it is written over is not defined behavior.

  • 1
    Not to the operating system. The standard says nothing about an OS. Not every implementation has an underlying OS and even those that do may manage it totally within the C library itself. Other than that, a good answer. – paxdiablo Sep 02 '13 at 23:45