4

I was wondering if a malloc() call is tied to its initial variable that you assign malloc() to at all, when it comes to how the system frees the memory.

For example, can I do the following:

void * ptr1 = malloc(50);
void * ptr2 = ptr1;
ptr1 = malloc(25);
free(ptr2);

I was intending to free the memory that was initially assigned to ptr1, but later free it by another pointer.

Muzol
  • 301
  • 1
  • 11
halfquarter
  • 107
  • 3
  • You must free every block that you malloc. It doesn't matter who's currently pointing to the block. You have successfully freed the 50-byte block here, but your 25-byte block is left dangling. – Lee Daniel Crocker Sep 06 '18 at 00:52
  • @LeeDanielCrocker: Must? – Eric Postpischil Sep 06 '18 at 01:05
  • Well, leaving reachable blocks at exit is not a problem, but it is evidence of sloppiness. – Lee Daniel Crocker Sep 06 '18 at 01:32
  • Misunderstanding: you don't `free` a *variable*, you `free` some memory zone previously obtanined by `malloc`, and it is a a *pointer value* (which could be kept in *several* variables of your C source code or none of them) – Basile Starynkevitch Sep 06 '18 at 19:44
  • @LeeDanielCrocker: One exception is in large applications where all desired context wind-down has been performed and system performance is valuable. Then there is no point in doing in-process bookkeeping that will just be wiped away when the process terminates. Of course,managing such “exit unclean” behavior is itself a challenge, since you do not want to forget allocated memory when merely cleaning up subtasks within the process rather than finally exiting. – Eric Postpischil Sep 07 '18 at 00:48

2 Answers2

11

Let's walk through this step-by-step (UNDEF means that we don't know what a value is; valid means a pointer is safe to use):

void *ptr1, *ptr2;    /* ptr1=UNDEF (invalid), ptr2=UNDEF (invalid) */
ptr1 = malloc(50);    /* ptr1=0xAAA (valid),   ptr2=UNDEF (invalid) */
ptr2 = ptr1;          /* ptr1=0xAAA (valid),   ptr2=0xAAA (valid)   */
ptr1 = malloc(25);    /* ptr1=0xBBB (valid),   ptr2=0xAAA (valid)   */
free(ptr2);           /* ptr1=0xBBB (valid),   ptr2=UNDEF (invalid) */

free() doesn't know which if any variable the pointer it's passed is stored in; it isn't guaranteed to (but also isn't guaranteed not to) update or interact with the variables in any way. All that effectively changes from an application developer's perspective is whether it's safe to actually use that pointer, or any other references into the block of memory allocated during the malloc() call that returned it.

As mentioned by @M.M in comments, the C language specification is explicit that the value of a pointer to a deallocated object is undefined, and the compiler is permitted to modify it in any way; see Why does MISRA C state that a copy of pointers can cause a memory exception? for further discussion.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • 1
    C11 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." . 7.22.3/1 says that the lifetime of a dynamically allocated object ends when it is deallocated, and 7.22.3.3 says that `free` causes the object to be deallocated. – M.M Sep 06 '18 at 03:11
2

Short answer: Yes

Longer answer:

You are not calling free "on the variable", but on the value stored in the variable.

To better understand what is going on, it may be better to think of memory as a big array of bytes, and visualizing a pointer as a numeric index into that array. And on most architectures you are likely to encounter, this is actually what is going on behind the scenes.

When you do

void * ptr1 = malloc(50);

malloc is reserving a block of 50 bytes and returning a pointer to that block. That pointer is nothing more than a numeric index telling us where in memory the reserved block starts.

In theory we could (on some architectures) write

int ptr1 = (int)malloc(50);

The reasons we are not doing it, are:

  1. sizeof(int) may not be large enough to hold a pointer
  2. void * tells the compiler that the numeric value stored in ptr1 should be treated as a memory address.

If we continue looking at your code:

void * ptr2 = ptr1;

There is nothing magical happening here. The "numeric value" stored in ptr1 is copied into ptr2, just as if ptr1 and ptr2 were normal integer variables.

ptr1 = malloc(25);

Here you overwrite the content of ptr1 with a new "numeric value", but the old value still exist as a copy in ptr2.

free(ptr2);

Here you call free with the value stored in ptr2. This is the value returned by malloc(50). free does not care which variable is holding that value/address. It only cares that the value/address points to the first byte of a block of memory that was reserved with malloc.

In theory, if you knew that malloc(50) returned the value 0xb00b1e5 you could do

free((void *) 0xb00b1e5);

But you can't safely predict what malloc is going to return, so don't do that.

HAL9000
  • 2,138
  • 1
  • 9
  • 20