Using realloc (X, 0) instead of free() and using malloc with length of a string +1

13,401

Solution 1

The behavior of realloc when the size is 0 is different in C11 (the current version). The standard says (7.20.3.1 for C11, 7.22.3.1 for C1x)

If the size of the space requested is zero, the behavior is implementation-defined: either a null pointer is returned, or the behavior is as if the size were some nonzero value, except that the returned pointer shall not be used to access an object

So, use free and don't rely on realloc.

When dealing with strings via char* always remember to include one extra character for the null terminator \0. This is the usual way to show where the string ends (the other being an explicit string length).

When using malloc and free remember that they must be matched exactly. You need to free the exact pointer (value) returned by malloc or realloc.

Solution 2

array = realloc (array, 0);

Realloc with a size of zero is equivalent to free() on some C implementations, but not all.

And the problem is that free doesn't work, it does not free the space used

Think about what char **array means and how it is allocated in your application. Often pointers-to-pointers are used as two-dimensional arrays, expressed as an array of arrays. Most applications allocate these with multiple calls to malloc(). array is simply an array of char *, where each element of that array is an array of char. Simply calling free() on the array of char * will free the array of char *, but not each of the arrays of char.

You need to call free() multiple times as described here.

I tried with different numbers and it works everytime, but if I don't do that +1 it does not work correctly.

C strings are null terminated, which means that the program keeps track of where the string ends by putting a nul character at the end of the string. That means that a C string of length N needs space for N characters, plus one nul character. The overall length of the memory space is then N+1.

Solution 3

First question:

realloc(array, 0) is not equivalent to free(array).

The standard (C99, §7.20.3.4 ¶1) says:

The realloc function deallocates the old object pointed to by ptr and returns a pointer to a new object that has the size specified by size.

and gives no "special case" for a size==0; so, you are getting a pointer to an object of size zero - but which potentially is still an object, and still has to be freed.

Interestingly, I think the realloc may simply fail in such a circumstance, returning NULL; in this case, in your code the memory is leaked, since, when realloc fails, it does not free the original memory block you passed to it (which is the reason why you never do array = realloc(array, size) but you always use an intermediate variable to check for NULL in order to avoid memory leaks).

Actually, the standard does specify the size==0 implementation-defined behavior for all the memory allocation functions, not just for malloc as I remembered; so, the behavior is implementation defined, as desribed below:

More intuitively, realloc is "conceptually equivalent" to to malloc+memcpy+free on the other pointer, and malloc-ing a 0-byte chunk of memory returns either NULL either a unique pointer, not to be used for storing anything (you asked for 0 bytes), but still to be freeed. So, no, don't use realloc like that, it may work on some implementations (namely, Linux) but it's certainly not guaranteed.

Also, it's not clear how you deduced that free doesn't work. I can think of two ways you may have been convinced of this:

  1. the value of array and of the data it points to is unchanged;
  2. the allocated memory in the task manager/top/whatever doesn't decrease.

For the first case, that's normal; when you free a pointer, it doesn't magically get wiped - your pointer still points to where it pointed, but that memory is no longer yours - it's now back to the C runtime, which will probably re-give it away in a future malloc. That's why that thing is called a "dangling pointer", and many people after a free set it to NULL to avoid writing again in a space of memory that has already been released.

As for the second, it's common policy for allocators not to give back memory to the operating system immediately (unless we are talking about really big chunks); the idea is that probably the application will need such memory again soon, and keeping that memory for the current process can avoid continuous system calls to take/give memory from the OS. Since system utilities for monitoring the memory used normally can only see what the OS has given to the process, it's normal that they don't show any memory usage decrease.

By the way, remember that, if you char ** array contains pointers to stuff allocated with malloc, you have to free them first, otherwise you're leaking memory.


Second question:

C strings are null-terminated, i.e. the last character of the string is always a \0 to mark the string ends, while strlen gives you the length of the string excluding the null terminator. So, if you don't add that +1 you are allocating one char less than the memory needed to actually store your string.


Addendum

By doesn't work I mean that it did crash (the problem might have been somewhere else ofc) but when I changed it to realloc(X, 0) it did work, in the sense of deleting used dynamic used memory

As the manpage says,

Crashes in malloc(), calloc(), realloc(), or free() are almost always related to heap corruption, such as overflowing an allocated chunk or freeing the same pointer twice.

You probably have some other bug in your code, but without seeing it it's impossible to tell what/where it goes wrong.

Solution 4

If you hope to maintain compatibility, realloc(p,0) is never equivalent to free(p), and zero allocations without subsequent frees are memory leaks plain and simple, even on Linux.

/* leak.c */
#include <mcheck.h>
#include <stdlib.h>
int main(int argc, char* argv[]) {
    void* p;
    mtrace();
    p = malloc(0x100);
    p = realloc(p, 0);
    exit(EXIT_SUCCESS);
}

$ cc -g leak.c -o leak
$ export MALLOC_TRACE=/tmp/t
$ ./leak
$ mtrace ./leak $MALLOC_TRACE

Solution 5

Regarding the second question:

In C, the string should be terminated by a null character '\0'. When you use strlen, this character is not counted but you need to allocate enough space for it (that's the +1).

If you try to print a string that does not contain a null character, a few "random" characters might be printed, but it can also crash. If you use strcpy, you'll do a buffer overflow.

Share:
13,401
keont
Author by

keont

Updated on June 26, 2022

Comments

  • keont
    keont almost 2 years

    So I don't really know how to put the title this time. First of all I'd like to say that I've seen several comments on this page about warning if the question is related to "homework". Mine is, but it's also completed and I just want to further understand what is going on with the code.

    I have also read posts and books for some time, but I think I am still missing things.

    I have 2 lines of code I don't quite understand in the code I worked with. The work is about getting whatever file is used as argument (if it's 0 files, it read from stdin), and print it on the standard output backwards. All of this, talking about C as I tried to put in the tag.

    First problem is this:

    array = realloc (array, 0);
    

    Where array is defined as

    char **array;
    

    And the problem is that free doesn't work, it does not free the space used (maybe I used it wrong? In other place I have known how to use it, but not this time). With the testing I have done and what I have read, I believe that realloc is doing the same, but I'm no 100%.

    Second one is:

    char* alloc = malloc (strlen ((char*)string)+1);
    

    Where alloc is used to copy the exact length of the line I am going to put into an array, so I can, after that, just print the text backwards.

    And the question is why I have to use that +1. I mean if I don't use for some reason it doesn't work, I tried with different numbers and it works everytime, but if I don't do that +1 it does not work correctly.

    I know probably the question is too vague and bad written to really be answered but again, I'm not sure about that and I did my best to explain myself (english no mother tongue as it's probably obvious).

  • alk
    alk almost 11 years
    If free(p) wouldn't have worked because of the scenario you are describing, then realloc(p, 0) wouldn't have worked either.
  • Andrei
    Andrei almost 11 years
    realloc(p,0) is not equivalent with free(p) in C11
  • hpsMouse
    hpsMouse almost 11 years
    realloc(p,0) is equivalent to free(p) only on Linux, not in standard C, nor POSIX.
  • Marc Brooker
    Marc Brooker almost 11 years
    I fixed the statements in the post about realloc and free.
  • Andrei
    Andrei almost 11 years
    The standard does provide a special case for size==0, it's just that it is the same for all the memory management functions. In the n1256 draft it is at section 7.20.3.1
  • Matteo Italia
    Matteo Italia almost 11 years
    @Andrei: you are correct, fortunately, it's what I already stated about malloc :) fixing the answer...
  • keont
    keont almost 11 years
    Ok thanks, I should have understand that myself, since in the test we look for that kind of strings... That was my bad, my problem was that I began writing +8, then +9, because I thought it was a matter of number of bits. Now I believe I get it.
  • R.. GitHub STOP HELPING ICE
    R.. GitHub STOP HELPING ICE almost 11 years
    If it's equivalent on Linux, that's a bug. realloc(p,0) is formally equivalent to free(p); malloc(0);, and on Linux (well, glibc), malloc(0) returns a unique non-null pointer for each call, not a null pointer.