2. C FUNDAMENTALS

advertisement
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).
Download