elbecker
elbecker

Reputation: 75

Does free() work on a reassigned pointer?

Let's say I have the following code:

int *ptr = (int*) malloc(0), *ptr2;
ptr2 = (int*) malloc(4 * sizeof(int));
*(ptr2 + 1) = 3;
ptr = ptr2;
free(ptr)

Does calling free(ptr) work on the new memory block ptr is pointing to or on the null pointer?

Upvotes: 1

Views: 170

Answers (2)

Craig Estey
Craig Estey

Reputation: 33601

Yes, in your example ptr is set to ptr2 which comes from malloc.

So, free(ptr); is valid (e.g. just as if we did free(ptr2);).

But, now, we've lost the original value of ptr so the block from the first malloc is now a memory leak. That is, no variable has the original value so it can't ever be freed.

To fix that, but retain your original code, we can do:

int *ptr = (int *) malloc(0), *ptr2;

ptr2 = (int *) malloc(4 * sizeof(int));
*(ptr2 + 1) = 3;

// to prevent a leak of the first malloc ...
int *ptr3 = ptr;

// without ptr3, this would "leak" the original value of ptr
ptr = ptr2;
free(ptr)

// free the first block ...
free(ptr3);

Side note: malloc returns void *, which works for any pointer type, so no need to cast the return value. See: Do I cast the result of malloc?

So, in the code do (e.g.):

ptr2 = malloc(4 * sizeof(int));

There is still some extra replication of code. The sizeof(int) would have to be changed if we ever changed the type of ptr2.

So, to "future proof" the code, many people prefer:

ptr2 = malloc(sizeof(*ptr2) * 4);

UPDATE:

You might also add a note about malloc(0) having implementation defined behavior. – chqrlie

Yes, malloc(0) has implementation defined behavior. Some possibilities:

  1. Returns NULL. IMO, the best option
  2. Treats the allocation internally as malloc(1)
  3. Returns a special "zero length" allocation.

I'd avoid the use of malloc(0) for those reasons. It's "fragile" and of marginal utility.

I've [mostly] seen it used by novice programmers that plan to use realloc in a loop and believe that they can't call realloc on a NULL pointer.

However, realloc will accept a NULL pointer just fine.

For example, if we were going to read a file filled with integers into an array and we didn't know how many numbers we had in the file, we might do:

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

int
main(int argc,char **argv)
{

    if (argc < 2)
        exit(3);

// NOTE: novices do this ...
#if 0
    int *ptr = malloc(0);
// NOTE: experienced programmers do this ...
#else
    int *ptr = NULL;
#endif

    // number of elements in the array
    size_t count = 0;

    // open the input file
    FILE *input = fopen(argv[1],"r");
    if (input == NULL) {
        perror(argv[1]);
        exit(4);
    }

    while (1) {
        // increase array size
        ptr = realloc(ptr,sizeof(*ptr) * (count + 1));

        // out of memory ...
        if (ptr == NULL) {
            perror("realloc");
            exit(5);
        }

        // decode one number from file
        if (fscanf(input,"%d",&ptr[count]) != 1)
            break;

        // advance the count
        ++count;
    }

    // close the input stream
    fclose(input);

    // trim array to actual size used
    ptr = realloc(ptr,sizeof(*ptr) * count);

    // print the array
    for (size_t idx = 0;  idx < count;  ++idx)
        printf("%zu: %d\n",idx,ptr[idx]);

    // free the array
    free(ptr);

    return 0;
}

Note: There are some rare occasions with special circumstances where doing malloc(0) does make sense. Usually, where the pointer has to be passed to some code that will discern a NULL vs. malloc(0) vs. regular allocation. But, they are an advanced usage and I wouldn't recommend them for a beginner.

Upvotes: 6

Captain Hatteras
Captain Hatteras

Reputation: 519

free(ptr) will release the four "ints" ptr2 points to. Changing memory that is unallocated doesn't make it allocated.

I will warn you that there is a memory leak here. The memory originally pointed to by ptr will still be allocated but unreferenced.

Upvotes: 0

Related Questions