views:

629

answers:

7

Consider the following C code:

int main(){  
    int* c;  
    c = (int*)malloc(sizeof(int));  
    c = 0xdeadbeef;  
    free(c);  
    return 0;  
}

This will segfault because you are trying to free c, which is not something that has been malloc'ed before. My question is what happens to the block i just malloc'ed? Obviously c is not pointing to it anymore, so it can't be used, but is it still considered part of the 'free' list, or is this an explicit memory leak?

+11  A: 

It's a leak. It'll be reclaimed by the OS when your program terminates.

tpdi
The traditional Unix garbage collection scheme: run the program, and reclaim the memory when it terminates. Doesn't work well with IDEs and browsers and other programs that are likely to be running a long time.
David Thornley
+1  A: 

That code won't segfault, assuming you change c = 7; to *c = 7;

Majd Taby
free(c) may segfault, since free() will deref the junk pointer.
Michael
Majd, you are technically correct (the best kind of correct), but changing the pointer to an extremely invalid address and attempt to dereference it was rather his whole point.
CaptainAwesomePants
well not if you change c = 7 to *c = 7. It is a bit out of topic but he is right
Ben
Oh sorry, I thought that was a syntax error :)
Majd Taby
+10  A: 

The memory is still allocated, causing a memory leak. Would you want it any other way? There really isn't a way for the machine/compiler to know that the memory that you allocated should be reclaimed. If this were not the proper behavior, your code would function probabilistically: you could never really trust the code.

You can always re-point to that block of memory at some time in the future, so freeing it automatically would pull the rug out from under you.

Tony k
Well, for languages other than C, there are things called garbage collectors that do it for you.
Dan Fego
Yes, but this is C. :)
Tony k
A: 

Note that the value returned by malloc() doesn't need to be casted, since the conversion void* -> int* is automatic.

You can also rewrite the call like this:

c = malloc(sizeof *c);

So now if you change c's type, you don't have to rewrite the allocation at all.

[Edit]

It would also be a good idea to check that the allocation succeeded (i.e. c != NULL).

Bastien Léonard
Although in line 4, a cast to (int*) is needed, or else you'll get a warning.
CaptainAwesomePants
For C, I do recommend leaving the (int *) or whatever off the malloc(). It isn't necessary, and it covers up the possible error of not having #included <stdlib.h>.
David Thornley
+1  A: 

After the malloc(), c is a variable holding a memory address. In this case, c has the value of the memory address of the first byte you allocated.

When you say c = 7, you are saying "c is now pointing to memory address '7' ". Because, in this case, memory address "7" was not allocated to your process, you cannot free() it. It is theoretically possible that memory address "7" (or 0xa73c930bf, or whatever you had set it to) was indeed allocated to your process, in which case the free() would be successful.

After you change the value of c, however, the memory is still allocated. Your process still has the memory and its data - you've just lost the pointer. You don't know where that memory starts.

This is an okay situation. In C, you might have different variables pointing to that memory address. You might even have an "int", rather than an "int*" storing that memory address. C does not attempt to keep track of whether or not you have stored that particular memory address - which is just a number, really. Doing so would would be an impossible task, since any attempt to keep track would require losing some of flexibility C pointers offer.

So to answer your question: since you have no way of keeping track of the value of c, this program has a memory leak. And by that, I mean that you have memory that cannot possibly be used by your program, and is thus wasting resources.

When the program exits, however, then all memory that has been allocated to your process is freed and made available for other programs to use.

rascher
It may be possible to store an address in an int, but it's a bad idea. There is no guarantee that int and int* have the same size and the same representation.
Bastien Léonard
Bear in mind you can try to free() it, and the implementation may actually execute the free() code, changing the memory. This is almost always a Bad Thing, since it did something not well defined on some chunk of memory that may be important to the rest of the program.
David Thornley
If you want to store a pointer in an integer type variable, intptr_t is what you want to use. intptr_t is big enough to store a pointer of any type.
sigjuice
A: 

Please don't flame me, but I'm not clear what the point of your question is. Clearly what you're doing is in direct conflict with what the language intends for you to do. It's equivalent of saying "what would happen if I filled the gas tank of my car with fire extinguisher fluid, just because the nozzle of the extinguisher happens to fit in the gas tank hole".
I'm not trying to be a jerk, I'm just not clear on why THIS particular question? I can think of an endless battery of questions that take advantage of the power of pointers and wonder about how many ways there are to use them incorrectly, resulting in application failure. Is there something you're trying to accomplish that your code *would be doing, if only you could figure out the right way to do it? Or are you wondering if there's some internal mechanism that keeps track of your pointers and helps you reclaim that memory if you accidentally lose track of it? (If so, that question was answered above).

Dr.Dredel
+1  A: 

Well, imo, the segfault you get at this point is not because you try to free memory which you have not allocated before, the segfault occurs because you try to reference a memory address which the operating system has not granted you permission you to you (that is the definition of a segmentation fault).

Some experiments, say you would run your sample code in valgrind, you would get this as output:

    ==6945== Invalid free() / delete / delete[]
    ==6945==    at 0x402265C: free (vg_replace_malloc.c:323)
    ==6945==    by 0x80483D5: main (bla.c:6)
    ==6945==  Address 0x7 is not stack'd, malloc'd or (recently) free'd
    ==6945== 
    ==6945== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 11 from 1)
    ==6945== malloc/free: in use at exit: 4 bytes in 1 blocks.
    ==6945== malloc/free: 1 allocs, 1 frees, 4 bytes allocated.

So this is a memory leak 'pur sang'. Now suppose you would change the code so the pointer you try to free is 'near' the pointer you allocated (so the operating system still know you have access to it, operating system don't grant memory access on byte boundaries). Say we modify the code like this:

int main(){  
    int* c;  
    c = (int*)malloc(sizeof(int));  
    c++;
    free(c);  
    return 0;  
}

When running this application you would not longer get a segmentation fault (emitted by the kernel) but a warning from glibc (the owner of malloc() and free() )

    edb@Flying-Spaghetti-Monster:/tmp$ ./a.out 
    *** glibc detected *** ./a.out: free(): invalid pointer: 0x0804a00c ***
    ... followed by a trace

So you're trying to free some memory of which the kernel know it belongs to you, but of which glibc can't remember handing it out to you. If you would run this in valgrind (which operates by replacing the free(), malloc(), realloc(), ... function in libc and performing accounting on its own) you would get this output:

    ==6955== Invalid free() / delete / delete[]
    ==6955==    at 0x402265C: free (vg_replace_malloc.c:323)
    ==6955==    by 0x80483D2: main (bla.c:5)
    ==6955==  Address 0x418a02c is 0 bytes after a block of size 4 alloc'd
    ==6955==    at 0x4022AB8: malloc (vg_replace_malloc.c:207)
    ==6955==    by 0x80483C0: main (bla.c:3)
    ==6955== 
    ==6955== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 11 from 1)
    ==6955== malloc/free: in use at exit: 4 bytes in 1 blocks.
    ==6955== malloc/free: 1 allocs, 1 frees, 4 bytes allocated.
amo-ej1
+1 for having FSM in your bash prompt.
Chris Lutz