I have been scratching my head regarding the memory 'behavior' with arrays decayed as pointers.
I have a function where an array of structures is created in a function (without explicit memory allocation) and then passed to another function as a pointer. Said function store the pointer in a static variable.
Reading this kind of code, I would say the pointer should be invalidated at the end of the first function (since no malloc was done) yet it is not.
However, calling a free() on this pointer throws a glibc error : invalid pointer. Makes sense, since no malloc was called.
Is there some implicit memory allocation being performed because of the decaying array ?
Is there a way to properly free the memory after this ? I know the trivial answer would be to allocate the memory myself, I am mainly asking out of curiosity.
Edit : some dummy code as requested :
static structure* s_array = NULL;
void foo()
{
structure array[5];
bar(array); // array decaying as a pointer
}
void bar(structure* ptr)
{
s_array = array; // pointer stored in the static, not invalidated at the end of foo()
}
void freeBar()
{
free(s_array); // invalid pointer
}
I would say the pointer should be invalidated at the end of the first
function (since no malloc was done) yet it is not.
Yes, it is, but "invalidation" in C just means that subsequent behavior is undefined ... anything can happen, including your program appearing to work perfectly.
However, calling a free() on this pointer throws a glibc error : invalid pointer. Makes sense, since no malloc was called.
Indeed it makes sense since malloc wasn't called. You can only free memory that was malloced; freeing memory that isn't is undefined behavior, and in this case the library was kind enough to produce a diagnostic.
Is there some implicit memory allocation being performed because of the decaying array ?
There's no such thing as a "decaying array". Arrays aren't first class objects in C so they cannot be passed to functions ... only their addresses can. The "decay" simply consists of the name of the array being converted to a pointer to the first element of the array when used in expression context ... it's a compile-time thing.
Is there a way to properly free the memory after this ?
The memory was "freed" at the end of the first function; nothing else need be done ... other than not storing its address.
Related
I tried to compile the following code :
#include <stdio.h>
void main()
{
int *p = (int*) malloc(4*sizeof(int));
p++;
p++;
free(p);
}
But it gives me the following error.
* Error in `/home/a.out': free(): invalid pointer: 0x0000000001d04018 *
Aborted.
I think I am still within the allocated limits of size 4 ints. But then why invalid pointer. Can some one help.
You are invoking undefined behavior.
From free manual:
The free() function frees the memory space pointed to by ptr, which must have been returned by a previous call to malloc(), calloc() or realloc(). Otherwise, or if free(ptr) has already been called before, undefined behavior occurs.
It doesn't matter whether you are inside allocated region or not. The pointer must be the same that was allocated before.
The rationale for being it undefined is that typical implementation (this is implementation defined) must remember other metadata related to the allocation, e.g. the allocation size, in some data structure in order to free them later. If ptr is not same as returned by allocation function then there is no way to lookup into this data structure to get those metadata during freeing.
The free() function can only operate on a pointer that has been directly allocated with malloc() or any of its companions.
If you change the pointer you'll have errors like this, that modified pointer was never allocated.
"Close enough" does not count. It must be identical to the pointer you got in the first place.
The contract for malloc and free is fairly simple. The pointer value (the address) you pass to free must be exactly a pointer value you got from malloc.
Exactly, and not in the same block, however close. It must match exactly. You modify the pointer before passing it back (you increment), so you violate that contract. This is going to cause undefined behavior. And you experience it in the from of a crash. It could be worse, it could appear to work. Until it blows up in your face later for no apparent reasons. That's the nature of undefined behavior. Avoid it.
I was reading to Dangling Pointer and found it's a good habit do this, to prevent oneself from dangling pointer error.
free(ptr); // free the ptr
ptr = NULL;
Now I decided to test this with a sample vanilla C code.
CASE_1
char *ptr = malloc(10);
...
...
free(ptr);
ptr=NULL;
// Just to check what happen if I call free more than I once
free(ptr)
ptr=NULL;
All work fine. Until I decided to wrap the free and the pointer NULL assignment in a function I named it safefree
void safefree(char *pp) {
free(pp);
pp = NULL;
}
CASE_2
Now, when I ran the above method more than 1 (like this)
safefree(ptr);
safefree(ptr);
I get the following error.
malloc: *** error for object 0x7fd98f402910: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
I happen to understand the error (pointer being freed was not allocated)
but I fail to understand why it doesn't fail in the CASE_1 and fails in latter part of the sample code.
First, let's review the behaviour of free(). Quoting C11, chapter ยง7.22.3.3
void free(void *ptr);
The free function causes the space pointed to by ptr to be deallocated, that is, made
available for further allocation. If ptr is a null pointer, no action occurs. Otherwise, if
the argument does not match a pointer earlier returned by a memory management
function, or if the space has been deallocated by a call to free or realloc, the
behavior is undefined.
Follow the two emphasized points.
You can pass a null pointer, NULL, to free() as many times as you want, they are valid calls (just to be ignored).
You cannot pass a pointer which has already been passed to free() once.
Case 1:
Here, after calling free() with the pointer, we're explicitly setting the pointer to NULL. That is why, calling free() with the same pointer variable later, any number of time, is not an issue, at all.
case 2:
C uses pass-by-value for function argument passing. That's why, When wrapped inside a function,
free(pp);
works as expected, (it passes the required pointer to free()) but
pp = NULL;
is local to the function and that change is not reflected to the caller. Thereby, calling the function again causes double free, as now,
the pointer has already been passed to free()
the assigned NULL is not reflected to the caller, hence the pointer is not set to NULL
in the next call, we're passing already-free()d pointer again.
As already mentioned, this invokes undefined behavior.
Solution: You need to pass a pointer to the pointer as the argument of the called function safefree() and from the called function, you can set the pointer value to NULL to get it reflected in the caller. Something like
void safefree(void ** ptrToPtr)
{
free(*ptrToPtr);
*ptrToPtr= NULL;
}
and calling like
safefree (&ptrToBeFreed);
will do the job (mind the types there, anyways).
Aside from Jonathan's remark, if you do something that results in undefined behavior (free the pointer again), it results in undefined behavior (errors, crashes).
Doing the same thing in different contexts does not necessarily result in the same undefined behavior, it depends on implementation details; they are typically only known to the compiler developer, so they seem 'random' from the outside. Nothing much can be learned by analyzing random undefined behavior. It could for example be that the compile time affects the result..., or the spelling of the directory you work in... anything.
I pass a pointer of a given type to a function.
In the function I allocate the memory needed:
Pointer = (mytype * ) malloc (N* sizeof (mytype));
And it all goes well. After the function ends another one calls pointer.
But the previously filled pointer is now without memory.
Shouldn't pointer have kept its filled memory?
Or does the ending of a function deallocate the memory?
Sorry but I am unable to paste my code because I work on a non connected PC.
No. Memory allocated by malloc is not deallocated at the end of a function.
Otherwise, that would be a disaster, because you would be unable to write a function that creates a data structure by allocating memory for it, filling it with data, and returning it to the caller.
No, but you're not returning the pointer to the caller. The argument inside the function is not the same as the value at the calling site, so changing it by assigning the return value from malloc() doesn't change the caller's value.
This:
Foo *foo;
AllocateAFoo(foo);
has no chance of changing the value of foo after the function returns, even if the argument is assigned to inside the function. This is why malloc() returns the new value.
You need to do that also:
mytype * Allocate(size_t num)
{
return malloc(num * sizeof (mytype));
}
This means that there's no point in sending the uninitialized pointer from the caller to the function, so don't.
Also, you shouldn't cast the return value of malloc() in C.
Also, you need to be aware that malloc() is just a function like any other. How would you write a function that reacts when execution leaves other functions? The answer is of course "you can't", and thus malloc() can't either.
You can use alloca(), but that's not supported on all architectures.
I have a c function that looks like this
void fn(void *data) {
type *p=malloc(sizeof *p);
p=data;
...
free(p);
}
If I understand correctly, the two problems with this are that the malloc sets aside some memory for p, but the pointer in p is then immediately overwritten by the pointer data so nothing is pointing to the memory allocated to p. Also, the subsequent free actually frees the memory at data so that whatever function called fn will no longer be able to safely access it. Is this right?
What I probably meant to do is:
void fn(void *data) {
type *p;
p=data;
...
}
Since there's no malloc there's nothing to free and the pointer in data continues to point to allocated memory?
EDIT: I should point out that I know for sure that the pointer in data is actually a pointer of the same type as p.
FURTHER EDIT: The pointer in data points to something that was malloc'd elsewhere.
Yes. You understood right. Assigning data to p after allocating memory to p will leave no pointer to the allocated memory by malloc.
A block of memory that's no longer accessible to a program is said to be garbage. A program that leaves garbage behind has a memory leak.
Unfortunately, unlike some other languages, C doesn't have garbage collector.
Another thing is that calling free(p) will invoke undefined behavior because the argument to free must be a pointer that was previously returned by a memory allocation function (or it could be a NULL pointer).
Yes, the function should not free the memory it did not allocate. The principle worth following in most cases is: do not allocate and deallocate in different contexts.
Consider the c code:
void mycode() {
MyType* p = malloc(sizeof(MyType));
/* set the values for p and do some stuff with it */
cleanup(p);
}
void cleanup(MyType* pointer) {
free(pointer);
pointer = NULL;
}
Am I wrong in thinking that after cleanup(p); is called, the contents of p should now be NULL? Will cleanup(MyType* pointer) properly free the memory allocation?
I am coding my college assignment and finding that the debugger is still showing the pointer to have a memory address instead of 0x0 (or NULL) as I expect.
I am finding the memory management in C to be very complicated (I hope that's not just me). can any shed some light onto what's happening?
Yes that will free the memory correctly.
pointer inside the cleanup function is a local variable; a copy of the value passed in stored locally for just that function.
This might add to your confusion, but you can adjust the value of the variable p (which is local to the mycode method) from inside the cleanup method like so:
void cleanup(MyType** pointer) {
free(*pointer);
*pointer = NULL;
}
In this case, pointer stores the address of the pointer. By dereferencing that, you can change the value stored at that address. And you would call the cleanup method like so:
cleanup(&p);
(That is, you want to pass the address of the pointer, not a copy of its value.)
I will note that it is usually good practice to deal with allocation and deallocation on the same logical 'level' of the software - i.e. don't make it the callers responsibility to allocate memory and then free it inside functions. Keep it consistent and on the same level.
cleanup will properly free p, but it won't change its value. C is a pass-by-value language, so you can't change the caller's variable from the called function. If you want to set p from cleanup, you'll need to do something like:
void cleanup(MyType **pointer) {
free(*pointer);
*pointer = NULL;
}
And call it like:
cleanup(&p);
Your code is a little bit un-idiomatic, can you explain a bit better why you want to write this cleanup function?
Yes
Yes
Yes: There is a block of memory magically produced by malloc(3). You have assigned the address of this memory, but not the memory itself in any meaningful way, to the pointer p which is an auto variable in mycode().
Then, you pass p to cleanup(), by value, which will copy the pointer and, using the copy local to cleanup(), free the block. cleanup() then sets it's own instance of the pointer to NULL, but this is useless. Once the function is complete the parameter pointer ceases to exist.
Back in mycode(), you still have pointer p holding an address, but the block is now on the free list and not terribly useful for storage until allocated again.
You may notice that you can even still store to and read back from *p, but various amounts of downstream lossage will occur, as this block of memory now belongs to the library and you may corrupt its data structures or the data of a future owner of a malloc() block.
Carefully reading about C can give you an abstract idea of variable lifetime, but it's far easier to visualize the near-universal (for compiled languages, anyway) implementation of parameter passing and local variable allocation as stack operations. It helps to take an assembly course before the C course.
This won't work as the pointer in cleanup() is local, and thus assigning it NULL is not seen by the calling function. There are two common ways of solving this.
Instead of sending cleanup the pointer, send it a pointer to the pointer. Thus change cleanup() as follows:
void cleanup(MyType** pointer)
{
free(*pointer);
*pointer = NULL;
}
and then just call cleanup(&p).
A second option which is quite common is to use a #define macro that frees the memory and cleans the pointer.
If you are using C++ then there is a third way by defining cleanup() as:
void cleanup(MyType& *pointer)
{
// your old code stays the same
}
There are two questions are here:
Am I wrong in thinking that after
cleanup(p); is called, the contents of
p should now be NULL?
Yes, this is wrong. After calling free the memory pointed by the pointer is deallocated. That doesn't mean that the content pointed by the pointer is set to NULL. Also, if you are expecting the pointer p to become NULL in mycode it doesn't happen because you are passing copy of p to cleanup. If you want p to be NULL in mycode, then you need a pointer to pointer in cleanup, i.e. the cleanup signature would be cleanup(MyType**).
Second question:
Will cleanup(MyType* pointer) properly
free the memory allocation?
Yes, since you are doing free on a pointer returned by malloc the memory will be freed.
It's not just you.
cleanup() will properly clean up your allocation, but will not set the pointer to NULL (which should IMHO be regarded as separate from cleanup.) The data the pointer points to is passed to cleanup() by pointer, and is free()ed properly, but the pointer itself is passed by value, so when you set it to NULL you're only affecting the cleanup() function's local copy of the pointer, not the original pointer.
There are three ways around this:
Use a pointer to a pointer.
void cleanup(struct MyType **p) { free(*p); *p = NULL; }
Use a macro.
#define cleanup(p) do { free(p); p = NULL; } while(0)
or (probably better):
void cleanup_func(struct MyType *p) { /* more complicated cleanup */ }
#define cleanup(p) do { cleanup_func(p); p = NULL; } while(0)
Leave the responsibility of setting pointers to NULL to the caller. This can avoid unnecessary assignments and code clutter or breakage.