/* ** $Id: malloc.c 789 2008-02-23 06:27:49Z phf $ ** ** Examples for allocating/freeing memory; sorry no time ** to add very many comments here. :-/ (Note: In Spring ** 2008 we did some of this earlier already.) */ #include #include // This is a node in a linked list that we play with a // little below. Each node has a pointer to the next // one in the list, next == NULL means we are at the // last node. Each node also carries around some data, // just an int in our case. struct node { struct node *next; int data; }; // A function to print out a list in a somewhat nice // format. Note that this is recursive, but we could // easily write an iterative version as well. (Which // I did in lecture in Spring 2008.) void print_list( struct node *head ) { if (head != NULL) { // An actual node in the list, so we print it... printf( "[%d]->", head->data ); // ...and then print the rest of the list! print_list( head->next ); } else { // The empty list (or the NULL pointer at the end). puts( "[]" ); } } // The main function... int main( void ) { // Sometimes we don't know ahead of time how many // variables ("boxes") we need for a certain program. // So we have to make them dynamically, while the // program is running. In C we use the function // malloc(3) to allocate memory, and we need to tell // it how much memory we want. This works even for // things like plain ints, although it's not the // most frequent application. But let's start with // that. Before, we did stuff like this: int i = 10; // make a box "i" and fill it with "10" int *pi = &i; // pointer to the box "i" printf( "%d\n", i ); printf( "%p\n", pi ); printf( "%d\n", *pi ); *pi = 5; printf( "%d\n", i ); printf( "%p\n", pi ); printf( "%d\n", *pi ); puts( "----------" ); // But that assumes we wrote down a box like "i" which // is what we want to avoid. So let's call malloc(3) // to make a new box of the right size (enough for // holding an int value): pi = malloc( sizeof(int) ); // This *could* go wrong if we're out of memory for some // reason, so we need to check if we actually got a box. if (pi != NULL) { // Now we refer to an "anonymous" box made by malloc // using the pointer pi; let's make sure that this is // a new box by setting it to something and checking // the value of the box "i" as well. printf( "%d\n", i ); printf( "%p\n", pi ); printf( "%d\n", *pi ); *pi = 47; printf( "%d\n", i ); printf( "%p\n", pi ); printf( "%d\n", *pi ); // Once we're done using a dynamically allocated box, // we need to free(3) it again; if we forget, we "leak" // memory, losing it "forever"; if we "leak" long enough, // we eventually run out of memory. There is no garbage // collector in C. Java's been too nice to you! :-) free( pi ); // At this point, *pi doesn't belong to us anymore, we // gave it back to the system. However, let's check pi: printf( "%p\n", pi ); // It *still* has the same value! We call "pi" a dangling // reference since it is pointing to something that does // not exist (or at least not belong to us) anymore; aside // from memory leaks, dangling references are another very // common source of bugs in C programs. To be safer, always // set the pointer to NULL right after freeing it. pi = NULL; // Now you will at least notice if you accidently use the // pointer (dereferencing it will segfault). printf( "%p\n", pi ); } else { puts( "Out of memory! Argh!" ); } puts( "----------" ); // Note that malloc(3) does not guarantee that the memory // you get is initialized in any particular way; calloc(3) // does, look it up in the man pages. :-) pi = calloc( 1, sizeof(int) ); // TODO: check error! printf( "%d\n", *pi ); *pi = 47; printf( "%d\n", *pi ); free( pi ); pi = NULL; puts( "----------" ); // Now let's play with the idea of a linked list for a // little while. First we need a pointer to the start // of the list, and since the list initially is empty, // it points nowhere for now. struct node *head = NULL; print_list( head ); // To insert a first node, we must make the node, which // is what alloc is good for; once we have it, we need // to initialize it with the required data. struct node *new = malloc( sizeof(struct node) ); // TODO: check error! new->next = NULL; new->data = 47; // Now we have to get the node onto the list, which is // pretty easy at this point: We simply point head to // the one and only node we have so far. head = new; print_list( head ); // Let's insert another node. We're inserting at the // beginning of the list because that's simpler to do // (we don't have to find the end first). We certainly // need to malloc() a node and fill it's data field. new = malloc( sizeof(struct node) ); // TODO: check error! new->data = 42; // At this point, however, we need to initialize the // next field for the new node. We want it at the front // of the list, so *after* it is inserted, it should // point to whatever is *currently* the first element. // But that's simply head! So: new->next = head; // Similarly, since we want the new node to be the first // one, we have to adjust head itself to the new first // node. Easy as well: head = new; print_list( head ); // Let's do another one, as you'll see the pattern is // the same: new = malloc( sizeof(struct node) ); // TODO: check error! new->data = 37; new->next = head; head = new; print_list( head ); // If we want to remove the first node, we can't just // do this: head = head->next; print_list( head ); // It *seems* to work, but we just leaked memory since // we didn't *free* the node we took out of the list. // So while it is not part of the list anymore, it still // takes up space. However, we can't do this either: free( head ); head = head->next; print_list( head ); // The problem *now* is that we're accessing a dangling // reference, namely head->next! The "head" variable is // not *our* anymore after we free it! This could crash // quite horribly, here it works most of the time... But // it's still *wrong*! The only right way to do this: // Remeber the node following the one we're about to delete. struct node *t = head->next; // Free the node itself, handing it back to the system. free( head ); // Adjust the head field to skip the node we just freed. head = t; print_list( head ); return EXIT_SUCCESS; }