From: "John M. Aldrich" 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 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit To: djgpp AT delorie DOT com DJ-Gateway: from newsgroup comp.os.msdos.djgpp Precedence: bulk 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... | ---------------------------------------------------------------------