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

6

12

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).

Harmonium answered 26/5, 2013 at 13:46 Comment(8)
realloc(array, 0) is not equivalent to free(array). This idiom is just wrong. I don't have time to write an answer at the moment but hopefully someone can explain it well.Stodder
How have you concluded that free doesn't work?Witwatersrand
Please explain what is meant by "doesn't work". Does the program crash? What error messages are printed?Choroid
Note that the semantics of free is make memory available for subsequent allocations and not decrement some free space counter reported by ps, memfree or similar system utility.Typecast
@R.. I can't really defend myself, I know I have read it does work the same way, or so I understood. So, links: linux.die.net/man/3/realloc The realloc() function returns a pointer to the newly allocated memory, which is suitably aligned for any kind of variable and may be different from ptr, or NULL if the request fails. If size was equal to 0, either NULL or a pointer suitable to be passed to free() is returned. If realloc() fails the original block is left untouched; it is not freed or moved.Harmonium
@n.m. 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 memoryHarmonium
If your program crashes on free, there is a bug in your code. realloc(X, 0) may or may not be equivalent to free(X). It is implementation-dependent. On your system they are obviously not equivalent because free crashes and realloc does not. By disabling the crash you simply pretend the bug does not exist. It is however still there. I would recommend trying to find it.Choroid
BTW, array = realloc(array, newSize); is bad. If the realloc fails, the old memory won't be freed but NULL will be returned and clobber the old pointer, leaving you with a memory leak. Correct is tmp = realloc(array, newSize); if (tmp == NULL) {Do something appropriate here, keeping in mind that array still points to the original block and is still valid.} else {array = tmp;}.Bronchiectasis
E
12

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.

Exodontics answered 26/5, 2013 at 14:13 Comment(0)
C
5

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.

Colorimeter answered 26/5, 2013 at 13:59 Comment(5)
If free(p) wouldn't have worked because of the scenario you are describing, then realloc(p, 0) wouldn't have worked either.Lustre
realloc(p,0) is not equivalent with free(p) in C11Exodontics
realloc(p,0) is equivalent to free(p) only on Linux, not in standard C, nor POSIX.Maser
I fixed the statements in the post about realloc and free.Colorimeter
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.Stodder
H
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.

Helga answered 26/5, 2013 at 14:13 Comment(2)
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.1Exodontics
@Andrei: you are correct, fortunately, it's what I already stated about malloc :) fixing the answer...Helga
I
1

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
Illuminator answered 11/9, 2013 at 15:49 Comment(0)
S
0

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.

Sent answered 26/5, 2013 at 13:52 Comment(1)
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.Harmonium
W
-1

The problem lies in char **array !! How is that built? Sounds like it's not a continuous block of mem and it was built like :

char **array  = malloc(sizeof(char*)*size);
for (size_t i=0; i< size; i++) {
    array[i] = malloc(strlen(string)+1);
}

If this is the case then each array index has it's individual pointer to random memory blocks! In that case before you cleanup the array, you need to free each individual index!!!

for (size_t i=0; i<size; i++) {
    free(array[i]);
}
free(array);

Also with realloc, a NULL can also mean failure, therefore you're dangling the old pointer!. never do

foo= realloc(foo, new_size);

do

if (tmp = realloc(foo, new_size) ) {
     foo = tmp;
} else {
   // clean your ram, how do you handle failure?
   free(foo);foo = NULL;
   // or you're ok without the new size,or try to realloc again
   // up to you
}
Welldressed answered 16/8, 2018 at 20:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.