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).
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
.
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.
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 free
ed. 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:
- the value of
array
and of the data it points to is unchanged;
- 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.
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.
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
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
}