chuck@umiami.ir.miami.edu (Chuck Urwiler) (04/07/91)
I'm writing in THINK C on the Mac. I need to know just how malloc() (calloc() actually) and free() work. Right now, free() doesn't seem to be working. I pass it all the nodes of a list to free() and then I try a new malloc() and get a totally different pointer - why? Shouldn't I get a pointer down low in memory again, or at least one of the old pointers? Maybe I just need to malloc() more stuff to see it work? That's not that important. I just want to know that if I calloc() a bunch of nodes, and then free() them, that I'm definitely freeing up the space the pointers used. I'm not into my own memory management yet, but if this doesn't work right, I guess I'm gonna hafta do my own. Any insightful comments, help, references, whatever - it would all be appreciated. And, since it seems this is a common misunderstanding on the net, maybe you should just go ahead and post replies. By the way, the THINK C manual says absolutely NOTHING about malloc() or free() or anything. Only a reference in the standard library manual. By the way, if you're a THINK C programmer, I just got the new Mac Programming primer volume two - for those of you wondering about the memory manager and Text Edit, this is the book to buy. (but it doesn't say anything about malloc() !) -Chuck- -- Chuck Urwiler University of Miami Music Engineering Voice & Keys =========================================================================== chuck@umiami -or @umiamivm (Bitnet) |"Sometimes, the one thing that you're chuck@umiami.ir.miami.edu (Internet) | looking for, it's the last thing on chuck@miavax.ir.miami.edu (Internet) | your mind" -Robbie Nevil curwiler (PAN) | =========================================================================== Disclaimer: I only work and learn at this University. I don't speak for it!
guido@cwi.nl (Guido van Rossum) (04/07/91)
(Question about what malloc and free do.) Don't worry, THINK C's malloc() and free() work fine. Freed memory is reused as you expect. The source is provided, but not much use since most of it is in assembly language, presumably for speed -- which they managed quite well in my opinion; in THINK 3.0 I had to use my own malloc() to get decent speed in a malloc-intensive application, but in 4.0 the library version was sufficient. What I understand from the assembly code is that it is a fairly thin layer on top of the ROM's NewPtr/DisposPtr interface, using zones somehow to keep many small blocks close together. The amazing thing is that free() is an almost direct interface to DisposPtr -- either its own administration looks the same as the ROM's, or it doesn't have an administration of its own, zones notwithstanding. The only bug that I see is that you can't realloc blocks to more than 15000 bytes (yes, this has bitten me a few times). You describe that you malloc() some blocks, the free() them, then malloc() some more, and expect to see the same addresses being reused, but instead see higher addresses being generated. This could either be a bug in your program (pointers are longer than ints, so you can't store pointers in ints or print them with %d or %x, and you *must* declare malloc; #include <stdlib.h> is the best way to do this), or a result from the strategy used by malloc when allocating small blocks (I can imaging that freed blocks go to the end of a rather long free list, while new blocks are allocated out of its beginning). --Guido van Rossum, CWI, Amsterdam <guido@cwi.nl> "What a senseless waste of human life"
risque@cs.uow.edu.au (Peter Castle) (04/08/91)
guido@cwi.nl (Guido van Rossum) writes: >(Question about what malloc and free do.) >Don't worry, THINK C's malloc() and free() work fine. I have had some trouble myself with Think C 4.0's malloc() and free(). Let me say what I think the problem is, and maybe some guru could respond. If you ask for more than 15000 bytes, malloc() just uses NewPtr to get it. However, if you want a smaller amount of memory, Think C asks for a 15000byte block and distributes smaller portions itself. Thus if you ask for lots of small amounts, only one call to NewPtr is made. However, free() just calls DisposPtr, so each of these small portions are released one at a time - never the whole 15000 bytes! So, if you ask for say 14k bytes at a time, memory is fragmented by these 1k leftover bits which are NEVER given back. I just use NewPtr and DisposPtr directly, just as Think C V3.0 used to do. A second problem: Has anyone ever used qsort to sort odd length records? Don't. The code for qsort AUTOMATICALLY adds one to the odd length of such records WITHOUT any warning. Sure, it is protecting the user from possible odd addresses being passed to the user's supplied comparison routine, but surely the errors caused by such a call would be a dead giveaway for any person silly enough to get such odd addresses. I was sorting odd length character records. Peter Castle Computer Science Dept University of Wollongong
phils@chaos.cs.brandeis.edu (Phil Shapiro) (04/13/91)
In article <3278@charon.cwi.nl> guido@cwi.nl (Guido van Rossum) writes: You describe that you malloc() some blocks, the free() them, then malloc() some more, and expect to see the same addresses being reused, but instead see higher addresses being generated. This could either be a bug in your program [...], or a result from the strategy used by malloc when allocating small blocks (I can imagine that freed blocks go to the end of a rather long free list, while new blocks are allocated out of its beginning). Actually, it's due to the fact that ThC uses a pretty simple method to allocate blocks of memory. It keeps track of the last place in the zone it looked at, and it starts looking for a free block of memory big enough there (it's called "startPtr"). This place isn't affected by freeing up blocks, so a freed block may not be reused for quite awhile. Freeing a block just changes its tag word to indicate that it's empty. I think that allocated blocks contain their size negated (~size) and free blocks just contain their size (or vice versa). So, free() just inverts the bits in the tag word to flag the block as free. Also, a free'd block may be combined with another free block if necessary. So, you may never see the original address again, even if it is reused. -phil
phils@chaos.cs.brandeis.edu (Phil Shapiro) (04/13/91)
In article <1991Apr7.224034.17484@cs.uow.edu.au> risque@cs.uow.edu.au (Peter Castle) writes: However, free() just calls DisposPtr, so each of these small portions are released one at a time - never the whole 15000 bytes! So, if you ask for say 14k bytes at a time, memory is fragmented by these 1k leftover bits which are NEVER given back. Actually, free() only calls DisposPtr if the pointer you're freeing was allocated by NewPtr. Here's a (possibly non-working) translation of the assembly code into C: void free(short *p) { if (p) { /* if the pointer's not nil, */ p[-1] = ~p[-1]; /* toggle the tag word which occurs before the start of your block */ if (!p[-1]) /* if the tag is zero, */ DisposPtr(p); /* it's a Mac pointer, so free it */ } } A second problem: Has anyone ever used qsort to sort odd length records? Don't. The code for qsort AUTOMATICALLY adds one to the odd length of such records WITHOUT any warning. Yes, this is true. It's a bug, and will be fixed in the next major release. It may make you feel better to know that all structures allocated by ThC are rounded up to an even word boundry, so the only time this bug will catch you is if you're sorting characters or strings. -phil