03-24-14 Dyn Mem Allo & Linked List
03-24-14 Dyn Mem Allo & Linked List
03-24-14 Dyn Mem Allo & Linked List
Lecture 23
Assignment 5 due on Friday
Topics for today
Dynamic memory allocation/2
Linked lists/1
Memory Allocation Functions
The <stdlib.h> header declares three memory
allocation functions:
mallocAllocates a block of memory but doesnt
initialize it.
callocAllocates a block of memory and clears it.
reallocResizes a previously allocated block of
memory.
These functions return a value of type void *
a generic pointer that could point to any type of
data object
It points to the block just allocated
The malloc Function
malloc() - allocates a block of size bytes on the heap and
returns a pointer to it:
void *malloc (size_t size); /* size is # of bytes needed */
mallocs return value can be stored in a variable of any pointer
type
When malloc cannot locate a memory block of the requested
size, it returns a NULL pointer (which you can test for).
int *p;
p = malloc(sizeof(int)); /* allocate memory for an int now */
if (p == NULL) { /* handle bad allocation error */ }
else *p = 0; /* a good idea to initialize its value */
Dynamically Allocated Strings
Dynamic storage allocation is often useful for
working with strings.
Strings are stored in character arrays, and it can
be hard to anticipate how long these arrays need
to be.
By allocating strings dynamically, we can
postpone the decision until the program is
running.
4
Dynamically Allocated Strings
malloc is especially useful to allocate enough
space for the result of a string operation:
E.g. dynamic concatonation
char *s1, *s2, *result;
/* assume s1 and s2 get string values */
result = malloc(strlen(s1)+ strlen(s2) + 1);
strcpy(result, s1);
strcat (result, s2);
dont forget to include room for the null character at
the end of a string.
Using Dynamic Storage Allocation
in String Functions
Dynamic storage allocation makes it possible
to write functions that return a pointer to a
new string.
For example writing a function that concatenates
two strings without changing either one.
The function will measure the lengths of the two
strings to be concatenated, then call malloc to
allocate the right amount of space for the result.
And then perform the operation and return a
pointer to the new string result
Dynamic Concatonation Fcn
char *concat(const char *s1, const char *s2)
{
char *result;
result = malloc(strlen(s1) + strlen(s2) + 1);
if (result == NULL)
{
printf("Error: malloc failed in concat\n");
getchar( );
exit(EXIT_FAILURE);
}
strcpy(result, s1);
strcat(result, s2);
return result;
}
A call of the concat function:
char *p;
p = concat("abc", "def");
Using Dynamic Storage Allocation
in String Functions
When the string that concat returns is no
longer needed, well want to call the free
function to release the space that the string
occupies.
void free (void *ptr);
E.g. from the last slide - free(p);
If we dont, the program may eventually run
out of heap memory.
Memory leaks
If we allocate memory but don't free it this is
called a "memory leak"
void my_function (void)
{
int *a;
a= malloc(100*sizeof(int));
/* Do something with a*/
/* Oops forgot to free (a); */
}
Every time we call this function it "steals" 100 ints worth of
memory. Why??
As we call this function more and more the heap memory
will fill up with unusable fragments.
Dynamically Allocated Arrays
Dynamically allocated arrays have the same
advantages as dynamically allocated strings.
The close relationship between arrays and pointers
makes a dynamically allocated array as easy to use
as an ordinary array.
Although malloc can allocate space for an array,
the calloc function is sometimes used instead,
since it initializes the memory that it allocates.
10
Using malloc to Allocate Storage for an Array
Suppose a program needs an array of n
integers, where n is computed during program
execution.
Well first declare a pointer variable:
int *a;
Once the value of n is known, the program can
call malloc to allocate space for the array:
a = malloc(n * sizeof(int));
Always use the sizeof operator to calculate
the amount of space required for each element.
Using malloc to Allocate Storage for an Array
We can now ignore the fact that a is a pointer
and use it instead as an array name
thanks to the relationship between arrays and
pointers in C.
For example, we could use the following loop
to initialize the array that a points to:
for (i = 0; i < n; i++) a[i] = 0;
We also have the option of using pointer
arithmetic instead of subscripting to access
the elements of the array.
12
The calloc Function
The calloc function is an alternative to
malloc.
Prototype for calloc:
void *calloc(size_t nmemb, size_t size);
Properties of calloc:
Allocates space for an array with nmemb
elements, each of which is size bytes long.
Returns a null pointer if the requested space isnt
available.
Initializes allocated memory by setting all bits to 0.
13
The calloc Function
A call of calloc that allocates space for an array a
of n integers:
int *a;
a = calloc(n, sizeof(int));
And then set the value of each element from input
printf("Enter each array element: ");
for (i = 0; i < n; i++)
{printf(next value:);scanf("%d", &a[i]);}
By calling calloc with 1 as its first argument, we can
allocate space for a data item of any type:
struct point{ int x, y; } *p;
p = calloc(1, sizeof(struct point));
The realloc Function
The realloc function can resize a previously
dynamically allocated array.
Prototype for realloc:
void *realloc(void *ptr,size_t newsize);
ptr must point to a memory block obtained by a
previous call of malloc, calloc, or realloc.
newsize represents the new size of the block, which
may be larger or smaller than the original size.
The realloc function allows us to make an array
grow or shrink as needed.
The realloc Function
Properties of realloc:
When it expands a memory block, realloc
doesnt initialize the bytes that are added to the
block.
If realloc cant enlarge the memory block as
requested, it returns a null pointer; the data in the
old memory block is unchanged.
If realloc is called with a null pointer as its first
argument, it behaves like malloc.
If realloc is called with 0 as its second
argument, it frees the memory block.
The realloc Function
We expect realloc to be reasonably efficient:
When asked to reduce the size of a memory block,
realloc should shrink the block in place.
realloc should always attempt to expand a memory
block without moving it.
If it cant enlarge a block, realloc will allocate a new
block elsewhere, then copy the contents of the old
block into the new one.
Once realloc has returned, be sure to update all
pointers to the memory block in case it has been
moved.
Deallocating Storage
Memory allocation functions obtain memory
blocks from the heap.
Calling these functions too oftenor asking
them for large blocks of memorycan
exhaust the heap, causing the functions to
return a null pointer.
To make matters worse, a program may
allocate blocks of memory and then lose track
of them, thereby wasting space.
Deallocating Storage
Example:
p = malloc();
q = malloc();
A snapshot after these two statements have
been executed:
What happens when we do:
p = q;
Deallocating Storage
After q is assigned to p, both variables now
point to the second memory block:
There are no pointers to the first block, so
well never be able to access it again.
Deallocating Storage
A block of memory thats no longer accessible
to a program is said to be garbage.
A program that leaves garbage behind has a
memory leak.
Some languages (e.g. Java) provide a
garbage collector that automatically locates
and recycles garbage, but C doesnt.
Instead, each C program is responsible for
recycling its own garbage by calling the free
function to release unneeded memory.
The free Function
Prototype for free:
void free(void *ptr);
free will be passed a pointer to an unneeded
memory block:
int *p, *q;
p = malloc(sizeof(int));
q = malloc(sizeof(int));
free(p);
p = q;
Calling free releases the block of memory
that p points to.
22
The Dangling Pointer Problem
Using free leads to a new problem: dangling pointers.
free(p) deallocates the memory block that p points to,
but doesnt change p itself.
If we forget that p no longer points to a valid memory
block, chaos may ensue:
char *p = malloc(4);
free(p);
strcpy(p, "abc"); /*** WRONG ***/
Modifying the memory that p points to is a serious error.
Dangling pointers can be hard to spot, since several
pointers may point to the same block of memory.
When the block is freed, all the pointers are left
dangling.
Dangling Pointers
Watch out for dangling pointers/2
int *p, *q;
p = malloc(sizeof(int));
q = p;
free(p); /* frees block both point to */
*q = 0; /* error */
Linked Lists
Dynamic storage allocation is especially useful
for building lists, trees, graphs, and other
linked data structures.
A linked list consists of a chain of structures
(called nodes), with each node containing a
pointer to the next node in the chain:
The last node in the list contains a null pointer.
We use another pointer to point to the first
node
first
Linked Lists
A linked list is more flexible than an array: we
can easily insert and delete nodes in a linked
list, allowing the list to grow and shrink as
needed, without having to move data around.
On the other hand, we lose the direct access
capability of an array:
Any element of an array can be accessed in the
same amount of time via an index.
Accessing a node in a linked list is fast if the node
is close to the beginning of the list, slow if its near
the end. We have to traverse the list for search.
Linked Lists
A node in a linked list might have the following
definition:
struct node
{ int data;
struct node *next; /* references next node in chain*/
};
. . .
struct node a, b, *first; /* variables */
The use of a struct tag is mandatory, since the node
structure contains a reference to itself.
first = &a;
a.data = 100;
a.next = &b;
b.data = 50;
b.next = NULL;
a b
100 first 50
Selection shortcut
Because pointers often point to structures, C
provides a special notation (the right arrow selection
operator ->) for selecting members of these
structures with a pointer variable
Its a combination of the * and . operators
E.g.
struct node *p;
. . .
p -> data = 100;
/* means the same as (*p).data = 100; */
Building a Linked List Node
An ordinary pointer variable points to the first node
to indicate the list is currently empty, the variable is
assigned a NULL
struct node *first = NULL;
New nodes can be created by calling malloc:
struct node *temp;
temp = malloc(sizeof(struct node));
The -> operator can be used to select a member (or
field) in the node that temp points to inorder to set it:
temp -> data = n; /*a value from the user */
temp -> next = NULL;
first = temp;
n
temp
first
data
next
Example 1
/*Create a linked list containing a series of numbers entered by the user:*/
struct node *first = NULL, *temp;
int n;
printf("Enter a series of numbers (enter -999 to stop):\n ");
scanf("%d", &n);
while (n != -999)
{ temp = malloc(sizeof(struct node));
temp->data = n;
temp->next = first;
first = temp;
printf("Enter the next number (enter -999 to stop):\n ");
scanf("%d", &n);
}
The numbers in the linked list are placed in the reverse of the order in which
they were entered.
What happens if the user enters:
10, 6, 3, 14, -999
struct node
{ int data;
struct node *next;
};
10 6 3 14
first