Memory Allocation Plus: Memory Allocation Heap versus Stack Where should we store stuff? • What’s wrong with this: char* getString() { char result[80]; …. Some code to put data into result … return result; } •Somehow, programs must modify or create data structures that persist in the program regardless of procedure calls… •So far the only variables we have seen (except for globals) are declared at the beginning of blocks and are “on the stack” •Variables “on the stack” disappear when the block they are declared in is exited or returned from. The answer is the heap… • What is it? – An area of memory distinct from the program stack – Access to it is controlled, not by the compiler, but by specific library procedures: • #include <stdlib.h> • malloc(), calloc() reserve blocks of memory for program use • free() returns blocks of memory to the heap for reuse Dynamic Storage Allocation • The use of memory in the heap is referred to as “dynamic storage allocation” • Arrays, strings, structures may all be stored in memory reserved in the heap area •Pointers are used to maintain references to objects allocated in the heap. •Objects in the heap may contain references to each other, forming what is known as a “data structure” •All objects in the heap are unaffected by actions taking place on the program stack The malloc Function • The most important storage allocation function is named malloc. When called, malloc allocates (reserves) a block of size bytes and returns a pointer to it: void *malloc(size_t size); • malloc returns a “generic” pointer that is capable of pointing at an object of any type. malloc’s return value can be stored in a variable of any pointer type: int *pi; pi = malloc(sizeof(int)); … or better yet… pi = (int *) malloc(sizeof(int)); For portability, it is best to use sizeof when calling malloc , you don’t want to have to remember.. malloc • malloc reserves a portion of the heap, and gives you a pointer to it to use as you see fit. It will not re-allocate that portion of heap memory until you “free” it. – The memory reserved is not initiallized – Be careful not to access the memory below or above what is allocated… Stay in bounds! • calloc() is a closely related function The Null Pointer • Since NULL is equivalent to 0, it tests false in if, while, do, and for statements: int *p; … if (p) … /* true if p is not NULL */ • It is illegal to apply the indirection operator to a null pointer: p = NULL; i = *p; /* illegal */ Dynamically Allocated Strings • malloc is can be used to dynamically allocate space for strings. char *result; result = malloc(strlen(s1) + strlen(s2) + 1); strcpy(result, s1); strcat(result, s2); return result; • Now, the string returned is in the heap and not on the stack. A pointer to it can be passed around and stored without concern about its memory location. •Warning: When using malloc to allocate space for a string, don’t forget to include room for the null character. Dynamically Allocated Arrays • An array can be dynamically allocated by a call to the calloc function. calloc() is similar to malloc, but allocates space for an array with n elements, each of which is size bytes long: void *calloc(int n, int size); • calloc will allocate n*size bytes, passing back a pointer to the beginning of the memory block it reserved. • calloc does one thing that malloc does not do: it will set every byte it allocates to zero, clearing the elements so you don’t have to. Dynamically Allocated Arrays • Example: int *a, size, i; printf("Enter array size: "); scanf("%d", &size); a = (int *) calloc(size, sizeof(int)); printf("Enter array elements: "); for (i = 0; i < size; i++) scanf("%d", &a[i]); • Warning: A program that uses malloc or calloc must include the following header file: #include <stdlib.h> The free Function • When a block of memory obtained by calling malloc or calloc is no longer referenced by a pointer, it is said to be garbage. In the following example, the memory pointed to by p becomes garbage when p is assigned a new value: int p = q = p = *p, *q; malloc(sizeof(int)); malloc(sizeof(int)); q; Garbage should be avoided; we need to “recycle” memory instead. Failure to do so causes “memory leaks” which will ultimately cause a program to fail when its heap is full (of garbage). The free Function • When a block of memory is no longer needed, it can be released back to the heap by calling the free function: int *p, *q; p = malloc(sizeof(int)); q = malloc(sizeof(int)); free(p); p = q; • Warning: Watch for “dangling pointers” left behind after a call to free: int *p, *q; p = malloc(sizeof(int)); q = p; free(p); *q = 0; /* error, writing to freed heap area */ Linked Lists • Dynamic storage allocation is useful for building lists, trees, graphs, and other linked structures. • A linked structure consists of a collection of nodes. Each node contains one or more pointers to other nodes. In C, a node is represented by a struct. • The simplest linked structure is the linked list, which consists of a chain of nodes, with each node pointing to the next node in the chain. A node in a linked list might have the following definition: struct node { int data; struct node *next; }; The use of a structure name is mandatory, since the node structure contains a reference to itself. Linked Lists • An ordinary pointer variable points to the first node in the list. To indicate that the list is empty, the variable can be assigned NULL: struct node *first = NULL; • Nodes could be allocated from the heap and added as a linked list: struct node* addNode(struct node* previous) { struct node* n = (struct node*) malloc(sizeof(struct node)); n->data = … initialize data … n->next = NULL; previous->next = n; return n; } addNode(first); The Right Arrow Operator • Nodes can be created by calling malloc: struct node *temp; temp = (struct node*) malloc(sizeof(struct node)); • The . operator could be used to select a data member in the node pointed to by temp: (*temp).data = n; • Because pointers often point to structures, C provides a special notation (the right arrow selection operator) for selecting members of these structures: temp->data = n; Example: Inserting into a Linked List • The following statements will create a node containing n, then insert it at the beginning of the list pointed to by first: struct node *temp; temp = malloc(sizeof(struct node)); temp->data = n; temp->next = first; first = temp; Example: Inserting into a Linked List • The following example creates a linked list containing numbers entered by the user: struct node *first = NULL, *temp; int n; printf("Enter a series of numbers: "); scanf("%d", &n); while (n != 0) { temp = malloc(sizeof(struct node)); temp->data = n; temp->next = first; first = temp; scanf("%d", &n); } The numbers will appear in the list in the reverse of the order in which they were entered. Deleting from a Linked List • The following statements will delete the first node containing n from the list pointed to by first, assuming that n is present in the list: struct node *p, *q; p = first; q = NULL; while (p != NULL) { if (p->data == n) { if (q) q->next = p->next; else first = p->next; free(p); break; } q = p; p = p->next; } Strings on the Heap • When you do something like this… char* getAString() { char s[80]; scanf(“%s”,s); return s; } String “s” is on the stack, and disappears when the function returns, leaving the return value pointing at who-knows-what. ***** This is a BAD THING! ***** Strings on the Heap • Instead, allocate strings from the heap… char* getAString() { char* s = (char*) malloc(sizeof(char)*80); scanf(“%s”,s); return s; } But there is a better way… Using a library function Strings on the Heap • Use the function “strdup”: char* getAString() { char s[80]; scanf(“%s”,s); return strdup(s); } strdup() takes a string as a paramenter, allocates len(string)+1 bytes from the heap, copies the string into the allocated memory and returns a pointer to the copy (the copy is in the heap, regardless of where the original string was).