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

char* test() {
    char* s = "Hello World";
    size_t len = strlen(s);
    char* t = malloc(sizeof(char)*(len+1));
    strcpy(t, s);
    free(t);
    return t;
};

int main(void) {
    printf("%s\n", test());
    return 0;
};

I would like to allocate and de-allocate memory inside the function. I tested this code and works, but I am wondering:

  • Why does this work?
  • Is it good practice to use the value of a freed pointer in main ?
F. Zer
  • 1,081
  • 7
  • 9
  • 2
    `free(t); return t;` << dangling pointer. Using it afterwards is undefined behavior and is allowed to have _any_ effect: it may work, it may crash, it may format your harddrive or it may connect to the Internet and order 100 pizzas to your door, and it doesn't have to do the same thing consistently every time you run it. – CherryDT Oct 07 '21 at 13:02
  • "Is it good practice to use the value of a freed pointer" - definitely not! Why would you think it is? – Stefan Riedel Oct 07 '21 at 13:04
  • Read [this](https://stackoverflow.com/questions/6441218/can-a-local-variables-memory-be-accessed-outside-its-scope/6445794), it's a C++ question but it's basically the same issue. _"Is it good practice to use the value of a freed pointer"_: it's not even _bad_ practice, it's blatantly wrong – Jabberwocky Oct 07 '21 at 13:05
  • 1
    When you were in school, did you ever have the experience of frantically trying to copy information off the blackboard **after** the teacher asked if anyone needed more time to copy it down, and you were too proud to put up your hand? That's pretty much what's going on here. – Wyck Oct 07 '21 at 13:22
  • @Wyck, would the teacher be `free` and the student `test` ? – F. Zer Oct 07 '21 at 16:41
  • `free` is when you answer "no" to the question of "do you need _this_ anymore?" (in the example, by passively not responding to the teacher. And _this_ is the writing on the blackboard that you're trying to copy down in your notebook). At that point, the teacher __might__ immediately wipe the board clean while you are trying to read it. Or the teacher might not. You have no control over when they erase the board or write something else. All you know is that you've _declared_ that you don't need to see the writing on the board any more. If you _lied_, then the trouble that ensues is on you. – Wyck Oct 07 '21 at 17:57
  • 1
    @Wyck, that is a very neat and clever explanation. Thank you so much. – F. Zer Oct 08 '21 at 14:35

3 Answers3

4

Once you call free on a pointer, the memory it pointed to is no longer valid. Attempting to use a pointer to freed memory triggers undefined behavior. In this particular case it happened to work, but there's no guarantee of that.

If the function returns allocated memory, it is the responsibility of the caller to free it:

char* test() {
    char* s = "Hello World";
    size_t len = strlen(s);
    char* t = malloc(sizeof(char)*(len+1));
    strcpy(t, s);
    return t;
};

int main(void) {
    char *t = test();
    printf("%s\n", t);
    free(t);
    return 0;
};
dbush
  • 205,898
  • 23
  • 218
  • 273
2

malloc reserves memory for use.

free releases that reservation. In general, it does not make the memory go away, it does not change the contents of that memory, and it does not alter the value of the pointer that held the address.

After free(t), the bytes of t still contain the same bit settings they did before the free. Then return t; returns those bits to the caller.

When main passes those bits to printf, printf uses them as the address to get the characters for %s. Since nothing has changed them, they are printed.

That is why you got the behavior you did with this program. However, none of it is guaranteed. Once free was called with t, the memory reservation was gone. Something else in your program could have used that memory. For example, printf might have allocated a buffer for its own internal use, and that could have used the same memory.

For the most part, malloc and free are just methods of coordinating use of memory, so that different parts of your program do not try to use the same memory at the same time for different purposes. When you only have one part of your program using allocated memory, there are no other parts of your program to interfere with that. So the lack of coordination did not cause your program to fail. If you had multiple routines in your program using allocated memory, then attempting to use memory after it has been released is more likely to encounter problems.

Additionally, once the memory has been freed, the compiler may treat a pointer to it as if it has no fixed value. The return t; statement is not required to return any particular value.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • I do not think the last paragraph is correct. Are you saying that all pointers to some block of memory that has just been freed might magically change to some other pointer values? (For example `void *a = malloc(42); void *b = a; free(b); if (b == a) printf("Hi!");` might not print "Hi!"?) – Ian Abbott Oct 07 '21 at 13:57
  • @IanAbbott: Yes, the value of the pointers may change. C 2018 6.2.4 says “The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime.” “Indeterminate” means that the value may appear to be different each time it is used. You do not need that intervening `b`; after `void *a = malloc(42); free(a);`, then `a == a` may evaluate as false. This can arise in two ways. One, in currently ancient or exotic C implementations, pointers may have needed “assistance” to work… – Eric Postpischil Oct 07 '21 at 14:08
  • … A pointer might be a handle to some data structure that contain additional information about where to find the pointed-to data or how to address it. When the memory is freed, that data structure is also released, and its contents may be altered. Using the “value” of the pointer may involve reading something from that data structure. Two, once C has this rule that the pointer becomes indeterminate, modern compilers may take advantage of the rule when optimizing… – Eric Postpischil Oct 07 '21 at 14:10
  • … Because the C specification says the value is indeterminate, the compiler does not have to actually load the pointer value when using it. So `a == a` might just compare registers r3 and r4 even though they have not been loaded with the contents of `a`. That particular outcome is unlikely, but any use of an indeterminate value can cascade to other code that optimizer is analyzing and result in elimination of some code that is essentially meaningless according to the technical definition of the C standard. – Eric Postpischil Oct 07 '21 at 14:11
  • @IanAbbott: However, my last paragraph was incorrect to say the behavior is undefined. Annex J.2 categorizes it has undefined, but it is non-normative. The normative text in 6.2.4 2 merely says the value is indeterminate, not that the behavior of using it is not defined. I have edited the paragraph accordingly. – Eric Postpischil Oct 07 '21 at 14:12
  • Lesson learned, thanks! – Ian Abbott Oct 07 '21 at 15:53
  • _After free(t), the bytes of t still contain the same bit settings they did before the free. Then return t; returns those bits to the caller._ This is not true in general. You simply don't know what has happened to the memory contents after it has been released. It depends on the implementation of `free()`. Some memory debuggers do change in order to know it has been released and cannot be used (e.g. valgrind) While it is not clear if you are talking of the bits of the pointer `t` or the bits where `t` still points, it is not clear from context what are the bits you are talking about. – Luis Colorado Oct 08 '21 at 10:07
  • @LuisColorado: The bytes of `t` are not the bytes of the memory it points to. That is not unclear; `t` is an object, and its bytes are the ones used to represent its value, the same as “the bytes of `t`” when `t` is an `int` or a `float`. Novices may conflate a pointer with what it points to, but I have corrected them elsewhere on this, and people knowledgeable about the C standard do not do this. Further, the text in the answer goes on to say the bits of these bytes are returned and are used by `printf` as the address, removing any possible ambiguity. – Eric Postpischil Oct 08 '21 at 10:47
  • Yeah, this is the reason I have made my comment. It is not clear from your explanation if you are talking of the bytes of `t`, which cannot be changed, as the parameters in C are all passed by value, or the bits of the memory allocated previously (which can, or not, depending on the implementation) – Luis Colorado Oct 08 '21 at 11:09
  • @LuisColorado: As I explained, the meaning is resolved unambiguously by the statement that the `printf` uses the bits as an address. – Eric Postpischil Oct 08 '21 at 11:14
1

It doesn't matter where do you free() a pointer. Once it is free()d, the pointer is not deferrenciable anymore (neither inside nor ouside the function where it was free()d)

The purpose of free() is to return the memory allocated with malloc() so the semantics are that, once you have freed a chunk of memory, it is not anymore usable.

In C, all parameters are passed by value, so free() cannot change the value expression you passed to it, and this is the reason the pointer is not changed into an invalid pointer value (like NULL) but you are advised that no more uses of the pointer can be done without incurring in Undefined Behaviour.

There could be a solution in the design of free() and it is to pass the pointer variable that holds the pointer by address, and so free() would be able to turn the pointer into a NULL. But this not only takes more work to do, but free() doesn't know how many copies you have made of the value malloc() gave to you... so it is impossible to know how many references you have over there to be nullified. That approach makes it impossible to give free() the responsibility of nullifying the reference to the returned memory.

So, if you think that free doesn't turn the pointer into NULL and for some strange reason you can still use the memory returned, don't do it anymore, because you'll be making mistakes.

You are adviced! :)

Luis Colorado
  • 10,974
  • 1
  • 16
  • 31