Author |
Message |
Rok Pape #1 / 14
|
 malloc/free questions
Hi Charles.
:>As I understand it, when malloc allocates a block of memory, it has to mark :>that block some how so that free will know how much memory to free. The :>pointer that malloc returns identifies the block and information about the :>block. It it the address of the block that is important, so doing something :>like changing the variable that has the address of the block is unimportant. :>However, changing the address malloc returns means losing access to the :>information about how the block was allocated. If you are interested, here is a portion of my malloc/free code (taken from .h file): #ifdef DEBUG #define FREESIG 0x12345678L #define USEDSIG 0x87654321L #define NONESIG 0x00000000L #endif #pragma pack(1) typedef struct _memBlock { #ifdef DEBUG ULONG ulSig; #endif ULONG ulSize; struct _memBlock *next; Quote: } memBlock, * pmemBlock;
#define BLOCK_OVERHEAD (sizeof(memBlock)) #pragma pack() Best regards, Rok Papez. Student at Faculty of Computer and Information Science, University of Ljubljana. --
|
Fri, 03 Nov 2000 03:00:00 GMT |
|
 |
Richa #2 / 14
|
 malloc/free questions
Quote: > I have some questions about malloc/free. While the case with which I am > working is more complex, the concepts can be tested with simple types. > (Yes, of course, in real code I would check malloc's return value.) > Case 1: freeing a pointer that has been modified since allocation > char *foo_p; > foo_p = malloc(100); > foo_p++; > free(foo_p); > /* Did I just leak? Or is this even legal? */
What you should get is an SIGBUS based on your attempt to perform a misaligned free(). If it succeeds, however, then you've leaked your first unit. Quote: > Case 2: freeing a pointer that is called by a different name > char *foo_p, *bar_p; > foo_p = malloc(100); > bar_p = foo_p; > free(bar_p); > /* Is this legal? */
Sure. It's a good idea, too, particularly if you want to free() the space pointed to by foo_p after diddling with it like in your first case. Quote: > Case 3: a combination, with function calls > char* get_mem(void) > { > return malloc(100); > }
I'm just being {*filter*}here, but return (char *)malloc(100); /* malloc(100 * sizeof(char)) */ Quote: > void release_mem(char* in_p) > { > free(in_p); > } > char *foo_p, *bar_p; > foo_p = get_mem(); > bar_p = ++foo_p; > release_mem(bar_p); > /* Legal? Leaks? What if the last line is */
It's a misaligned free() again. If it doesn't generate a SIGBUS, then it's a leak. Quote: > release_mem(foo_p); > /* ? */
It's a misaligned free(); bar_p is &(foo_p[1]) not &(foo_p[0]). -- All of reality is just a simulation running in God's computer. So chill. // If you'd like to drop me a note: net dot earthlink at huddler --
|
Fri, 03 Nov 2000 03:00:00 GMT |
|
 |
Jonathan Leffle #3 / 14
|
 malloc/free questions
Quote:
> I have some questions about malloc/free. While the case with which I am > working is more complex, the concepts can be tested with simple types. > (Yes, of course, in real code I would check malloc's return value.) > Case 1: freeing a pointer that has been modified since allocation > char *foo_p; > foo_p = malloc(100); > foo_p++; > free(foo_p); > /* Did I just leak? Or is this even legal? */
Your next allocation will probably crash if the free manages to return. At any rate, what you did here was incontestably unwise and unsupported and undefined. Quote: > Case 2: freeing a pointer that is called by a different name > char *foo_p, *bar_p; > foo_p = malloc(100); > bar_p = foo_p; > free(bar_p); > /* Is this legal? */
This is perfectly legal -- just don't free bar_p again or foo_p. Quote: > Case 3: a combination, with function calls > char* get_mem(void) > { > return malloc(100); > } > void release_mem(char* in_p) > { > free(in_p); > } > char *foo_p, *bar_p; > foo_p = get_mem(); > bar_p = ++foo_p; > release_mem(bar_p); > /* Legal? Leaks? What if the last line is */
You've incremented the pointer which is disastrous. Without the increment, all would be OK. With the increment, something is going to fail soon. Quote: > release_mem(foo_p);
Double freeing the space is disastrous too. Don't do it! Yours,
--
|
Fri, 03 Nov 2000 03:00:00 GMT |
|
 |
Daniel Barke #4 / 14
|
 malloc/free questions
Quote:
> >Case 1: freeing a pointer that has been modified since allocation > > char *foo_p; > > foo_p = malloc(100); > > foo_p++; > > free(foo_p); > > /* Did I just leak? Or is this even legal? */ > I think this will cause memory leak.
[snip] When considering what is and is not an error, it helps to know what malloc() and free() might be doing. In K&R's example of how to implement these functions (ed.2, section 8.7), malloc() accounts for memory in the following way. (Use a fixed-width font like Courrier to view the diagram.) HEADER _________________^_________________ / \ *********************************************************************** * * * * * POINTER TO * SIZE OF THE * BLOCK WHOSE ADDRESS IS * * NEXT FREE * BLOCK RETURNED * RETURNED BY malloc() * * BLOCK * TO THE USER * * * * * array of at least the * * Header * * unsigned int * requested size * * * * * *********************************************************************** ^ ^ ^ ^ | | | | | | | | | ((Header *) (foo_p+1)) - 1 foo_p | | MISALIGNED POINTER | | foo_p+1 ((Header *) foo_p) - 1 CORRECT ADDRESS OF HEADER The diagram shows an area of memory containing the block whose address is returned by malloc(), plus some adjacent house-keeping information used by malloc() and free(). The rough position of some relevant addresses is also given. free() expects to be able to find the header for the block whose address it is passed just before this in memory, i.e. at address (Header *) foo_p-1. But in the above code, free() is actually passed the address foo_p+1 rather than foo_p. free() will calculate the location of the start of the header incorrectly, leading to misbehaviour at run-time. This is just one way malloc() and free() might be implemented, but gives insight into how free()-ing an address NOT returned by malloc(), calloc() or realloc() can wreak havoc. Daniel Barker, Biocomputing Research Unit, Institute of Cell and Molecular Biology, Swann Building, King's Buildings, Mayfield Road, Edinburgh EH9 3JR UK --
|
Fri, 03 Nov 2000 03:00:00 GMT |
|
 |
Mike Albau #5 / 14
|
 malloc/free questions
: > Case 3: a combination, with function calls : > : > char* get_mem(void) : > { : > return malloc(100); : > } : I'm just being {*filter*}here, but Presumably you mean "{*filter*}retentive", but now maybe _I'm_... :-) : return (char *)malloc(100); /* malloc(100 * sizeof(char)) */ Exactly what does this accomplish? Are there still systems in wide use that declare malloc as something other than "void *malloc( size_t )"? If not, I'd like to make a plea for using the committe-given ability to assign a void * to any data pointer without a cast. Why? because "{*filter*}" casts are often a good tip-off to dubious code. If you get used to sprinkling casts through your code to "shut the compiler up", then someday you will cast an int** to an int* and since you used a cast, to say "Shut up, compiler, I know what I'm doing", but you _don't_ really know what you're doing, you will be in a world of hurt. I was under the impression that void * was invented partially for this purpose, but I'd gladly hear a reasoned argument to the contrary. My rule, not that I don't break it once in a while because I am a fallible being, is "no casts outside well-checked macros". Of course, I have to violate it in, e.g. cases where a function is declared to return a char * and take a const char * (strchr() springs to mind) The inability to specify such functions (or am I just stupid, please enlighten me) to "inherit" the qualifiers of their arguments is just one of those prices I pay for programming in a ubiquitous language. Mike
--
|
Sun, 05 Nov 2000 03:00:00 GMT |
|
 |
Bil Wendli #6 / 14
|
 malloc/free questions
[other okay cases deleted] : > Case 3: a combination, with function calls : > : > char* get_mem(void) : > { : > return malloc(100); : > } : I'm just being {*filter*}here, but : return (char *)malloc(100); /* malloc(100 * sizeof(char)) */ Having such a functions is a good thing for allocating and initilizing memory for an "object", such as a structure. The subsequent release_mem() function is good for the cleanup... : > void release_mem(char* in_p) : > { : > free(in_p); : > } --
|| Research Programmer --
|
Sun, 05 Nov 2000 03:00:00 GMT |
|
 |
Lawrence Kir #7 / 14
|
 malloc/free questions
Quote:
>> I have some questions about malloc/free. While the case with which I am >> working is more complex, the concepts can be tested with simple types. >> (Yes, of course, in real code I would check malloc's return value.) >> Case 1: freeing a pointer that has been modified since allocation >> char *foo_p; >> foo_p = malloc(100); >> foo_p++; >> free(foo_p); >> /* Did I just leak? Or is this even legal? */ >What you should get is an SIGBUS based on your attempt to perform >a misaligned free().
Alignment isn't the issue here. The problem is simply that the value being passed to free() is not a value previously returned by malloc/calloc/realloc (that has not subsequently been freed) or a null pointer. Quote: > If it succeeds, however, then you've leaked >your first unit.
There's no reason to believe that a memory leak will be the result. As a consequence of undefined behaviour *anything at all* can happen. It might even free the block of memory successfully in its entirety. Quote: >> Case 2: freeing a pointer that is called by a different name >> char *foo_p, *bar_p; >> foo_p = malloc(100); >> bar_p = foo_p; >> free(bar_p); >> /* Is this legal? */ >Sure. It's a good idea, too, particularly if you want to free() >the space pointed to by foo_p after diddling with it like in your >first case.
Note also tha after the call to free() the values of both foo_p and bar_p are indeterminate i.e. it is an error even to look at them (but you can assign new values to them). Quote: >> Case 3: a combination, with function calls >> char* get_mem(void) >> { >> return malloc(100); >> } >I'm just being {*filter*}here, but > return (char *)malloc(100); /* malloc(100 * sizeof(char)) */
The original is better. It is a bad idea to cast the return value of malloc since it doesn't buy you anything and it can mask (but not correct) errors such as forgetting to include <stdlib.h>. sizeof(char) is, by definition, 1 so it is normal to omit it as a constant factor in a context like this. Quote: >> void release_mem(char* in_p) >> { >> free(in_p); >> } >> char *foo_p, *bar_p; >> foo_p = get_mem(); >> bar_p = ++foo_p; >> release_mem(bar_p); >> /* Legal? Leaks? What if the last line is */ >It's a misaligned free() again. If it doesn't generate a SIGBUS, then >it's a leak.
Again, the result is undefined behaviour. A memory leak is what happens when you lose any way to reference allocated memory. In C that typically happens when you fail to free a malloc'd object before getting rid of all accessible pointer references to it. Memory leaks can eatup resources but are otherwise not an error and don't affect the behaviour of the program. The code above results in undefined behaviour which is a totally different situation. Quote: >> release_mem(foo_p);
Bad, however release_mem(foo_p-1); would be fine. -- -----------------------------------------
----------------------------------------- --
|
Sun, 05 Nov 2000 03:00:00 GMT |
|
 |
Richa #8 / 14
|
 malloc/free questions
Quote:
> : > Case 3: a combination, with function calls > : > > : > char* get_mem(void) > : > { > : > return malloc(100); > : > } > : I'm just being {*filter*}here, but > Presumably you mean "{*filter*}retentive", but now maybe _I'm_... :-) > : return (char *)malloc(100); /* malloc(100 * sizeof(char)) */ > Exactly what does this accomplish? Are there still systems in wide > use that declare malloc as something other than "void *malloc( size_t )"? > If not, I'd like to make a plea for using the committe-given ability to > assign a void * to any data pointer without a cast. Why? because "{*filter*}" > casts are often a good tip-off to dubious code. If you get used to sprinkling > casts through your code to "shut the compiler up", [....]
Who said anything about "sprinkling casts through code...to shut the compiler up"? Compiler warnings are your best friend. I acquired the habit, long ago, of explicitly casting the type from malloc(), calloc(), and realloc(). I agree that it's not necessary these days. Whether or not it represents dubious code, however, is another matter. I tend to think that the programmer doing such a thing is specifying exactly what s/he wants to occur, committee or no committee. Much like expressing (void)printf( .... ) ; does. As with any use of power, an explosion can result if the person using it is an idiot. -- All of reality is just a simulation running in God's computer. So chill. // If you'd like to drop me a note: net dot earthlink at huddler --
|
Sat, 11 Nov 2000 03:00:00 GMT |
|
 |
Richa #9 / 14
|
 malloc/free questions
Quote:
> >> I have some questions about malloc/free. While the case with which I am > >> working is more complex, the concepts can be tested with simple types. > >> (Yes, of course, in real code I would check malloc's return value.) > >> Case 1: freeing a pointer that has been modified since allocation > >> char *foo_p; > >> foo_p = malloc(100); > >> foo_p++; > >> free(foo_p); > >> /* Did I just leak? Or is this even legal? */ > >What you should get is an SIGBUS based on your attempt to perform > >a misaligned free(). > Alignment isn't the issue here. The problem is simply that the value being > passed to free() is not a value previously returned by malloc/calloc/realloc > (that has not subsequently been freed) or a null pointer.
Yes and no. From the standpoint of the C language, yes. From the standpoint of the underlying memory allocation mechanisms, no. See the
Quote: > > If it succeeds, however, then you've leaked your first unit. > There's no reason to believe that a memory leak will be the result. As > a consequence of undefined behaviour *anything at all* can happen. It > might even free the block of memory successfully in its entirety.
True. My answer was implementation-specific, rather than the one in the lanaguage specification. Quote: > >> Case 2: freeing a pointer that is called by a different name > >> char *foo_p, *bar_p; > >> foo_p = malloc(100); > >> bar_p = foo_p; > >> free(bar_p); > >> /* Is this legal? */ > >Sure. It's a good idea, too, particularly if you want to free() > >the space pointed to by foo_p after diddling with it like in your > >first case. > Note also tha after the call to free() the values of both foo_p and bar_p > are indeterminate i.e. it is an error even to look at them (but you can > assign new values to them). > >> Case 3: a combination, with function calls > >> char* get_mem(void) > >> { > >> return malloc(100); > >> } > >I'm just being {*filter*}here, but > > return (char *)malloc(100); /* malloc(100 * sizeof(char)) */ > The original is better. It is a bad idea to cast the return value of malloc > since it doesn't buy you anything and it can mask (but not correct) errors > such as forgetting to include <stdlib.h>. sizeof(char) is, by definition, 1 > so it is normal to omit it as a constant factor in a context like this.
In general, I agree. I've been casting that baby for a long time, however, and will continue to do so. Quote: > >> void release_mem(char* in_p) > >> { > >> free(in_p); > >> } > >> char *foo_p, *bar_p; > >> foo_p = get_mem(); > >> bar_p = ++foo_p; > >> release_mem(bar_p); > >> /* Legal? Leaks? What if the last line is */ > >It's a misaligned free() again. If it doesn't generate a SIGBUS, then > >it's a leak. > Again, the result is undefined behaviour. [...definitions...]
True. My answer was implementation-specific, rather than the one in the lanaguage specification. -- All of reality is just a simulation running in God's computer. So chill. // If you'd like to drop me a note: net dot earthlink at huddler --
|
Sat, 11 Nov 2000 03:00:00 GMT |
|
 |
Jerry Coff #10 / 14
|
 malloc/free questions
[ ... ] Quote: > I acquired the habit, long ago, of explicitly casting the type from > malloc(), calloc(), and realloc(). > I agree that it's not necessary these days. Whether or not it represents > dubious code, however, is another matter. I tend to think that the > programmer doing such a thing is specifying exactly what s/he wants to > occur, committee or no committee. Much like expressing > (void)printf( .... ) ; > does.
I agree -- both of them are ill-advised and foolish. If you're going to make explicit the fact that you're ignoring the value produced by printf, you should _clearly_ also make explicit the fact that you're ignoring the values produced by all the other things in your program. For example: x = 1; is _obviously_ BAD! To be explicit, what you really meant was that you were going to assign 1 to x, and then _ignore the value that produced_: (void)x=1; if one makes any sense at all, so does the other. Of course, in reality, both are absurd... -- Later, Jerry. The Universe is a figment of its own imagination. --
|
Mon, 13 Nov 2000 03:00:00 GMT |
|
 |
Peter J. Holz #11 / 14
|
 malloc/free questions
Quote:
>> : > return malloc(100); >> : return (char *)malloc(100); /* malloc(100 * sizeof(char)) */ >> If you get used to sprinkling >> casts through your code to "shut the compiler up", [....] >Who said anything about "sprinkling casts through code...to shut the >compiler up"? Compiler warnings are your best friend. >I acquired the habit, long ago, of explicitly casting the type from >malloc(), calloc(), and realloc().
You did. If you cast the return value of malloc to the required type, you will shut up almost any compiler, even when the conversion is dubious. As a practical example, let's consider a compiler where sizeof(int) != sizeof(void *), like the compilers for the good ole 8086 in some memory models (int == 16 bit, ptr == 32 bit) or the DEC Alpha (int == 32 bit, ptr == 64 bit). Lets say, you forgot to include <stdlib.h>, so the compiler assumes that malloc will return an int. If you don't use a cast, the compiler will probably complain that you are converting an int to a pointer. If you do use a cast, the compiler will assume that you know what you are doing, and keep quiet. The resulting code will replace half of the bits in the pointer with zeros, and you will get a core dump if you are lucky (a thrashed hard disk if you are unlucky). Now, I know, that most compilers have other warnings they might issue in this case ("implicit function declaration" for example), but why turn off a useful diagnostic if there is no need? hp -- _ | Peter J. Holzer | But now it's |_|_) | Sysadmin WSR | implementation-defined
__/ | http://wsrx.wsr.ac.at/~hjp/ | -- Clive Feather --
|
Mon, 13 Nov 2000 03:00:00 GMT |
|
 |
Lawrence Kir #12 / 14
|
 malloc/free questions
... Quote: >For example: > x = 1; >is _obviously_ BAD! To be explicit, what you really meant was that >you were going to assign 1 to x, and then _ignore the value that >produced_: > (void)x=1; >if one makes any sense at all, so does the other. Of course, in >reality, both are absurd...
Probably more so once you realise that this isn't the same thing as: (void)(x=1); :-) -- -----------------------------------------
----------------------------------------- --
|
Tue, 14 Nov 2000 03:00:00 GMT |
|
 |
Andreas Schwa #13 / 14
|
 malloc/free questions
|> (void)x=1; This must be (void)(x=1); -- Andreas Schwab "And now for something
--
|
Tue, 14 Nov 2000 03:00:00 GMT |
|
 |
Kaz Kylhe #14 / 14
|
 malloc/free questions
Quote:
>is _obviously_ BAD! To be explicit, what you really meant was that >you were going to assign 1 to x, and then _ignore the value that >produced_: > (void)x=1;
You mean (void)(x = 1); The cast has higher operator precedence than the assignment. :) You are right of course; if one is going to cast the discarded return values of functions like printf(), then to be consistent, one should cast all expression-statements to void. This is, of course, absurd to say the least. I also don't see how it can prevent errors. An error that could arise out of a failure to observe the return value of a function falls into two categories: the function could return a pointer (or other reference) to some object that was allocated in the function, in which case a memory leak results from ignoring the return value; and the function could return an error indicator which would cause a subsequent operation to fail. The first of these two cases is rare because the return value is of central interest to the surrounding code: it represents some allocated object, file or what have you, which is subject to further operations. The habit of writing (void) casts will help with neither of these errors, since it only encourages such discarding. Occurences of (void) printf(/*...*/) in acual programs are evidence of this. A robust program does in fact check the return value of printf, which is negative if an error occured. Such a check could be the difference between a program which silently discards data and one which reports that an error has occured (because the disk filled up, say). --
|
Tue, 14 Nov 2000 03:00:00 GMT |
|
|
|