Realloc on NULL-valued (or undefined) pointer

28,715

Solution 1

Is there any danger in allocating memory with realloc using the initially NULL-valued ptr

None

7.22.3.5

If ptr is a null pointer, the realloc function behaves like the malloc function for the specified size.

For the second part:

int* ptr; // no value given to ptr

would it be a problem to call realloc using ptr?

If you're using uninitialized pointers then that is a very serious problem indeed since you can't predict what their value will be. The function realloc only works correctly for NULL or values obtained from malloc / realloc.

Otherwise, if ptr does not match a pointer earlier returned by a memory management function [...] the behavior is undefined

Solution 2

With the specific code shown, there is no problem with using the null pointer initially.

If the variable ptr is uninitialized — not set to 0 or NULL — then any call to realloc() using it is dangerous; the behaviour is undefined and if you are lucky, the program will crash, but if you're unlucky, it will appear to work for a while, until something goes wrong later in the program where it will be hard to spot that the trouble is in code executed a long time ago.

There are those who argue it is better to use malloc() for the initial allocation and realloc() thereafter. There is some justice to the suggestion, not least because you probably wouldn't use ptr = realloc(ptr, 0); to free the memory, even though you could do so (so you don't really need malloc() or free() because realloc() can do all three operations). But the C90 standard requires realloc(0, new_size) to work equivalently to malloc(new_size), and I know of no C library that behaves differently (but there might be some; I've only used a few C libraries, albeit mostly the most widely used ones).


However, in a more general case such as the following code, then there is a subtle problem with the code (but it is not to do with the initial null pointer):

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

int main(void)
{
    char    *ptr = NULL;
    size_t   len = 0;
    char     buffer[256];

    while (fgets(buffer, sizeof(buffer), stdin))
    {
        size_t buflen = strlen(buffer) + 1;
        if (buflen > len)
        {
            if ((ptr = realloc(ptr, buflen)) == 0)  // Danger!
                // ... handle memory allocation failure ...
            len = buflen;
        }
        strcpy(ptr, buffer);
        // ... do something with ptr
    }
    free(ptr);
    return 0;
}

What is the danger? The danger is that if the second or a subsequent memory allocation fails and ptr is the only pointer to the allocated memory, you just overwrote its previous value with null. That means you cannot free the allocated memory using ptr any more — you've leaked memory. (For the first allocation, the initial value was 0, the overwritten value was zero, and nothing has changed; there is no memory leak. That's why the loop was added to the code.)

Rule of Thumb

  • Do not write ptr = realloc(ptr, newsize);

Save the new value into a separate variable until you've tested it.

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

int main(void)
{
    char    *ptr = NULL;
    size_t   len = 0;
    char     buffer[256];

    while (fgets(buffer, sizeof(buffer), stdin))
    {
        size_t buflen = strlen(buffer) + 1;
        if (buflen > len)
        {
            char *new_ptr = realloc(ptr, buflen);
            if (new_ptr == 0)
                // ... handle memory allocation failure ...
            ptr = new_ptr;
            len = buflen;
        }
        strcpy(ptr, buffer);
        // ... do something with ptr
    }
    free(ptr);
    return 0;
}

This code does not leak memory on an allocation failure.

Auxilliary recommendation: do not use a variable called new; it will make it difficult to compile with a C++ compiler. Even if you have no intention now of converting to C++ (and even though you would probably end up rewriting the memory management if you do), there's no virtue in using the C++ keyword new as a C variable name...unless you explicitly want to prevent compilation with a C++ compiler.

Solution 3

Is there any danger in allocating memory using realloc using the initially NULL-valued ptr?

No, that would exactly be like a malloc.

If instead of:

int* ptr = NULL;

I had this:

int* ptr; // no value given to ptr

would it be a problem to call realloc using ptr?

Yes, there would be a problem. If realloc doesn't get a NULL, it will try to expand memory starting from that location, or may try to free and malloc another part of memory. Since uninitialized variables can have any value, chances are very high, they are not a value realloc likes. If you are lucky, your program would immediately crash.

Share:
28,715
Admin
Author by

Admin

Updated on July 09, 2022

Comments

  • Admin
    Admin almost 2 years

    I was reading about realloc and got confused about a point mentioned there. Consider the code below:

    #include <stdio.h>
    #include <stdlib.h>
    
    int main () {
    
        int* ptr = NULL;
        ptr = realloc(ptr, 10*sizeof(int));
        return 0;
    }
    

    Is there any danger in allocating memory with realloc using the initially NULL-valued ptr? If instead of:

    int* ptr = NULL;
    

    I had this:

    int* ptr; // no value given to ptr
    

    would it be a problem to call realloc using ptr?

  • mk12
    mk12 over 11 years
    Note that the reason for this is that most implementations of malloc store the length of the block right before the pointer returned (allowing free to know how much memory to free). If you give realloc an uninitialized pointer, it would think it was a valid pointer (a pointer is a pointer, all realloc can do is trust you). This implementation would then try to interpret the few bytes (size_t) before it as the size of the block, which would obviously be incorrect. That is why you must explicitly null the pointer so it is known that it isn't a valid address.
  • Admin
    Admin over 11 years
    @Mk12: did you mean it stores the length of the block right after the pointer returned (or in better words, it stores the length of the block to which pointer points)? Sorry, I'm confused.
  • Admin
    Admin over 11 years
    This was a really beautiful answer. Unfortunately I already accepted another (also good) one...
  • mk12
    mk12 over 11 years
    @curvature: Suppose we have a very old computer, and its memory space consists of only 256 bytes. Pointers and size_t only needs to be 1 byte wide, because 1 byte can hold 256 distinct values. If you call malloc(13), it will find some memory. It will return you a pointer to memory address, say, 0x5, but it actually stores the number 13 right before it, in 0x4. That way when you call free on 0x5, it looks at the byte before it (0x4), sees that it contains the number 13, and then it knows it has to free 13 bytes (so it won't just free 0x5, it will also free 0x6, 0x7, 0x8, etc.)
  • mk12
    mk12 over 11 years
    So if you realloc an uninitialized pointer (which could point to anything), it will look at the byte right before it, and maybe it contains the value 103, who knows? After allocating the new memory, it will free 103 bytes there because it assumes that's what you used to have allocated that you now want to realloc.
  • supercat
    supercat about 7 years
    Usiing ptr = realloc(ptr,newsize is often fine in cases where a program would have no sensible way to continue usefully if the allocation fails, though wrapping realloc in a function that will output a diagnostic and call exit in case of failure may be more practical than having to scatter code all over the place to check the return value and abort in case of failure. It's too bad there's no function for the purpose of shrinking an allocation without invalidating any pointers to it, since code could simply assume that such a function would always succeed (even if the system can't...
  • supercat
    supercat about 7 years
    ...actually shrink the allocation for whatever reason, it could simply leave it as-is and user code wouldn't need to care).