[comp.sys.mac.programmer] THINK C's malloc

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