www.delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp/1998/08/12/19:46:19

From: "John M. Aldrich" <fighteer AT cs DOT net>
Newsgroups: comp.os.msdos.djgpp
Subject: Re: malloc()...free()...WTF??
Date: Wed, 12 Aug 1998 19:37:00 -0400
Organization: Two pounds of chaos and a pinch of salt.
Lines: 141
Message-ID: <35D2271C.6590C1E5@cs.net>
References: <6qr22o$rjn$1 AT birch DOT prod DOT itd DOT earthlink DOT net>
NNTP-Posting-Host: ppp208.cs.net
Mime-Version: 1.0
To: djgpp AT delorie DOT com
DJ-Gateway: from newsgroup comp.os.msdos.djgpp

Steven "Ionicis" An wrote:
> 
> OK, I understand that malloc() and free() allocate and free memory, but how
> does memory work in general?  Like, what's the heap and how does an array
> manage itself?  I might not sound too clear about wtf I wanna know, but that
> just shows how confused I am about memory, pointers, etc.  I know C pretty
> well...but it's just this memory allocation crap I can't understand...I
> don't understand.  Is there a page that explains how C programs handle
> memory?  Do I have to learn assembly to understand this?

Most C tutorials make an attempt to explain memory and pointers; a few
of them succeed.  I recommend personally the Waite Group's _New C Primer
Plus, 2nd edition_; it has one of the clearest and easiest to understand
treatments of pointers that I've ever seen.  Apart from that, the best
guide is experience - there's no real way to communicate the angst of
watching your program crash inexplicably for the hundredth time in a row
and being totally helpless to figure it out.  :-)

I'm going to assume that you're familiar with how memory works in a
computer - those 16, 32, 64, or however many megabytes of storage you
have to run programs in.  All programs take up a certain portion of that
memory when they are running; that's where the CPU retrieves
instructions and data from so that it can do what the program tells it
to do.

In an program that does not dynamically allocate memory (i.e., use
malloc(), free(), etc.), the amount of memory you have to work with can
be considered basically fixed.  When the program loads, it tells the
system exactly how much memory it needs to store itself in.  The system
sets aside the requested amount of memory for the program's exclusive
use and then reads the program into that memory.  All your program's
instructions and data are stored in that block of memory.

Sometimes, programs need additional memory to work with.  Say, for
example, that you're storing a list of names and addresses of all the
people you know.  You may know 10 people, or 1000 people... but consider
how you'd write such a program.  If you set aside space to store 10
people, you'd have to rewrite your program if you needed space for 15 or
20.  If you set aside space for 1000 people, and only put 10 or 20 in
it, you'd be wasting a huge amount of space... and you'd still need to
rebuild if you met the 1001st person.  This is known as a "static"
program model.

However, if your program is written dynamically, it can use space for as
many people as you need it to at any time, simply because, when you add
a new person, it goes to the operating system and requests that it be
given a little bit more memory to store the 11th, 101st, or 1001st
person in.  When you delete a person, the program can save the memory
for later use or return the memory to the system - that's a question of
program design.

I'll get to definition of terms:  the "heap" is the name for a portion
of memory set aside for a program to dynamically allocate as it sees
fit.  This is usually only an operative term in 16-bit code, because
32-bit code has the entire DPMI memory of the system as its "heap".  The
term sticks around, however.  When you allocate memory, you get it from
the heap, and when you free it, you return it to the heap.  The heap is
used in the dynamic program model.

Arrays are used mainly in the static model.  An array is given a fixed
size when the program is compiled, and the program's request for memory
includes the memory needed to store all arrays that are defined in that
program (automatic arrays are actually taken from the stack, but that's
fixed in size so it still applies to this discussion).  When you index
into an array, the program takes the address of the start of the array
(which is considered a constant during runtime), adds a certain number
of bytes to it (the index multiplied by the size of each array item),
and retrieves the array element from the resulting address in memory. 
Since arrays are stored in memory right along with all the other data in
your program, trying to index beyond the array bounds can damage other
data in your program, and is not recommended.

When you allocate memory via the malloc() function, you are requesting
dynamic memory from the program's heap.  If the available space is
sufficient, malloc() will get it, mark that it's being used by your
program, and return the address of the start of the block of memory. 
You must store that address into a pointer variable that indicates what
kind of data you want to store.  So, say, you want to allocate 1000
integers - your call to malloc() might look like this:

   int *plist;  /* pointer to an integer */
   plist = (int *) malloc( 1000 * sizeof(int) );

First you declare the pointer variable, and then you store an address in
it.  The typecast before malloc() is a mnemonic; it's not necessary in C
but is definitely required in C++.  You tell malloc() how much memory
you need by multiplying the number of items by the size of each.  If
malloc() is unable to satisfy your request, it will return a special
pointer called a NULL pointer.  You should test for this and take
appropriate action.

   if ( plist == NULL )
   {
       fprintf( stderr, "Unable to allocate memory.\n" );
       exit( 1 );
   }

That code says that if you couldn't get the requested memory, print an
error message and terminate your program.

Once you have memory, you can treat it just like any other kind of data,
except that the pointer points to the _first_ item in that block of
memory.  To get to any additional items, you can index the pointer as if
you were indexing an array (to the compiler, array labels and pointers
are identical save that you can't alter an array label).

   int ilist[1000];
   int *plist = (int *) malloc( 1000 * sizeof(int) );
 
   ilist[400] = 10;  /* ok */
   plist[400] = 20;  /* ok - pointers work just like arrays */
   ilist + 300 = 30; /* ok - arrays actually work like pointers */
   plist + 300 = 40; /* ok - pointer arithmetic implicitly multiplies
                        the index by the size of the element */
   plist = plist + 100;  /* ok - pointers can be changed */
   ilist = ilist + 100;  /* ERROR - can't alter an array label */

The other thing about dynamic memory is that you should get rid of it
when you're done with it.  The free() function exists for that purpose:

   free( plist );

Exiting a program automatically frees all allocated memory, but if your
program depends on lots of dynamic structures you should probably free()
any memory you don't need to prevent a very nasty condition called a
memory leak.  This can occur if you continue to allocate additional
memory without freeing what you don't need any more; your program will
eventually run out of memory and fail.

If you would like any more details, please email me in private; this
post is already more than long enough.  :-)

l8r
   
-- 
---------------------------------------------------------------------
|      John M. Aldrich       | A singer in a smoky room / Smell of  |
|       aka Fighteer I       | wine and cheap perfume / For a smile |
|   mailto:fighteer AT cs DOT net   | they can share the night / It goes   |
| http://www.cs.net/fighteer/| on and on and on...                  |
---------------------------------------------------------------------

- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019