4

I am learning C from the book by K.N.King and am going through the programming projects for chapter 17. I came across a self inflicted bug while I was completing the first project. It was undefined behaviour of the program since I was passing realloc() a copy of a pointer returned from an earlier malloc() call, since the realloc() call was occurring in a function other than main(). After re-reading parts of the chapter I realised that realloc() has undefined behaviour in this case.

I have tried to search why this is the case to no avail. So does anyone here know why the pointer passed to realloc() must be a pointer returned from an earlier call of malloc(), calloc() or realloc() and not a copy of these? Also, aren't we inherently always making a copy of a pointer returned by realloc() whenever we want to resize a memory block?

int num_parts = 0, max_parts = 10;

int main(void)
{
    struct part *inventory = malloc(max_parts * sizeof(*inventory));
    \\...
    switch(ch) {
        \\...
        case 'i':
            insert(inventory, &num_parts, &max_parts);
            break;
        \\...
    }
}

void insert(struct part *inventory, int *num_parts, *max_parts)
{
    if (*num_parts == *max_parts) {
        *max_parts += 10;
        struct part *temp = realloc(inventory, max_parts * sizeof(*temp));
        if (temp != NULL)
            inventory = temp;
        else
            printf("Error:...\n");
    }
    \\...
}
Zeta-Squared
  • 257
  • 2
  • 7

1 Answers1

4

The function insert accepts the pointer inventory by value

void insert(struct *inventory, int *num_parts, *max_parts)

and

insert(inventory, &num_parts, &max_parts);

It means that the function deals with a copy of the the value of the original pointer. Changing the copy within the function like

    if (temp != NULL)
        *inventory = temp;

does not influence on the value of the original pointer declared in main.

You need to pass the pointer by reference through a pointer to it.

That is

insert( &inventory, &num_parts, &max_parts);

And the function will look like

void insert(struct **inventory, int *num_parts, *max_parts)
{
    if (*num_parts == *max_parts) {
        *max_parts += 10;
        struct part *temp = realloc( *inventory, max_parts * sizeof(*temp));
        if (temp != NULL)
            *inventory = temp;
        else
            printf("Error:...\n");
    }
    \\...
}

In this case dereferencing the function parameter struct **inventory within the function we can get a direct access to the original pointer (object) and thus can change it.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • Ah ok. I thought this should be the same as arrays though, where by passing a function the name of the array we are only passing the pointer to the first element of the array and yet we can still manipulate the elements of the array this way? – Zeta-Squared Jun 04 '22 at 00:41
  • @Zeta-Squared In fact we pass elements of an array by reference through the pointer to the first element of the array. Using the pointer arithmetic and the dereference operator we can change any element of the array. We same way we need to pass a pointer by reference (through a pointer to it) if we are going to change its value in a function. – Vlad from Moscow Jun 04 '22 at 00:43
  • Yes, thank you I see now. In the case of an array, passing the array name to a function does not change what the pointer points to but rather the object which is being pointed to. In this case I have presented, since the memory block me be in a completely different location I need to be able to change what the pointer is pointing to and so I need to pass the address of the original pointer to the function, that is `struct part **inventory`. Thank you for your help, I have accepted your answer. – Zeta-Squared Jun 04 '22 at 00:55
  • @Zeta-Squared: Just to be clear, the problem is not that you passed `realloc` a copy of the pointer. That's fine, `realloc` can't tell the difference (if you began Vlad's function with `struct part *localinventory = *inventory;` and passed `localinventory` to `realloc`, it would work just fine). The problem was that you needed to replace the caller's original pointer, and you can't do that without a pointer to the caller's storage. – ShadowRanger Jun 04 '22 at 00:56
  • @ShadowRanger Yes I see that now. Thank you. – Zeta-Squared Jun 04 '22 at 01:04