Address and Pointers
Address and Pointers
For every variable declared in a program there is some memory allocation. Memory is specified
in arrays of bytes, the size of which depending on the type of variable. For the integer type, 2
bytes are allocated, for floats, 4 bytes are allocated, etc. For every variable there are two
attributes: address and value, described as follows:
Program
#include <stdio.h>
main ()
{
int i, j, k; //A
i = 10; //B
j = 20; //C
k = i + j; //D
Explanation
1. Memory allocations to the variables can be explained using the following variables:
100,i 10
200,j 20
300,k 30
When you declare variables i, j, k, memory is allocated for storing the values of the
variables. For example, 2 bytes are allocated for i, at location 100, 2 bytes are allocated
for j at location 200, and 2 bytes allocated for k at location 300. Here 100 is called the
address of i, 200 is called address of j, and 300 is called the address of k.
2. When you execute the statement i = 10, the value 10 is written at location 100, which is
specified in the figure. Now, the address of i is 100 and the value is 10. During the
lifetime of variables, the address will remain fixed and the value may be changed.
Similarly, value 20 is written at address 200 for j.
3. During execution, addresses of the variables are taken according to the type of variable,
that is, local or global. Local variables usually have allocation in stack while global
variables are stored in runtime storage.
Points to Remember
1. Each variable has two attributes: address and value.
2. The address is the location in memory where the value of the variable is stored.
3. During the lifetime of the variable, the address is not changed but the value may change
2. POINTERS (Contd)
C reveals its true power through pointer variables. Pointer variables (or pointers, as
they generally are called) are variables that contain addresses of other variables. All
variables you have seen so far have held data values. You understand that variables
hold various data types: character, integer, floating-point, and so on. Pointer
variables contain the location of regular data variables; they in effect point to the
data because they hold the address of the data. After you work with pointers for a
while, you will find that they are easier to use than arrays (and much more flexible).
A pointer is a variable whose value is also an address. As described earlier, each variable has two
attributes: address and value. A variable can take any value specified by its data type. For
example, if the variable iis of the integer type, it can take any value permitted in the range
specified by the integer data type. A pointer to an integer is a variable that can store the address
of that integer.
Pointers are variables. They follow all the normal naming rules of regular, nonpointer variables.
As with regular variables, you must declare pointer variables before using them. There is a type
of pointer for every data type in C; there are integer pointers, character pointers, floating-point
pointers, and so on. You can declare global pointers or local pointers, depending on where you
declare them.
About the only difference between pointer variables and regular variables is the data they hold.
Pointers do not contain data in the usual sense of the word. Pointers contain addresses of data.
Don't let the second operator throw you; you have seen the before. The means, of course,
multiplication. The asterisk is called an overloaded operator. Overloaded operators perform
more than one kind of operation depending on how you use them in your programs. C does not
confuse for multiplication when you use it as a dereferencing operator with pointers.
Any time you see the &used with pointers, think of the words "address of." The & operator
always produces the memory address of whatever it precedes. The operator, when used with
pointers, either declares a pointer or dereferences the pointer's value.
Declaring Pointers
Because you must declare all pointers before using them, the best way to begin learning about
pointers is to understand how to declare and define them. Actually, declaring pointers is almost
as easy as declaring regular variables. After all, pointers are variables.
If you need to declare a variable that will hold your age, you could do so with the following
variable declaration:
Declaring age like this does several things. C now knows that you will need a variable called
age, so C reserves storage for that variable. C also knows that you will store only integers in
age, not floating-point or double floating-point data. The declaration also requests that C store
the value of 30 in age after it reserves storage for age.
Where did C store age in memory? As the programmer, you do not really care where C decided
to store age. You do not need to know the variable's address because you will never refer to age
by its address. If you want to calculate or print with age, you will call it by its name, age.
TIP
Make your pointer variable names meaningful. The name filePtr makes
more sense than x13 for a file-pointing variable, although either name is
allowed.
Suppose that you want to declare a pointer variable. This pointer variable will not hold your age,
but it will point to age, the variable that holds your age. pAge might be a good name for the
pointer variable. Figure 1 illustrates what you want to do. The figure assumes that C stored age
at the address 350,606; however, your C compiler arbitrarily determines the address of age, so it
could be anything.
Figure 1:
pAge contains the address of age;
pAge points to the age variable.
The name pAge by itself has nothing to do with pointers, except that is the name you made up
for the pointer to age. pAge could just as easily have been named house, 43344,
space_trek, or whatever else you wanted to call it, just as you can name variables anything
(as long as the name follows the legal naming rules of variables). This reinforces the idea that a
pointer is just a variable that you must reserve in your program. Make up meaningful variable
names, even for pointer variables. pAge is a good name for a variable that points to age (as
would be ptrAge and ptrToAge).
however. Because of the dereferencing operator (),C knows that this is to be a pointer
variable.
Some programmers prefer to declare such a variable without a space after the , as follows:
Consider:
As long as p points to i, *p is an alias for i.
TIP
Whenever the dereferencing operator * appears in a variable definition, the
variable being declared is always a pointer variable.
Consider the declaration for pAge if the asterisk were not there: C would think you were
declaring a regular integer variable. The * is important, because it tells C to interpret pAge as a
pointer variable, not as a normal, data variable.
pAge is an integer pointer. This is very important. pAge can point only to integer values, never
to floating-point, double floating-point, or even character variables. If you needed to point to a
floating-point variable, you might do so with a pointer declared as
As with any automatic variable, C does not initialize pointers when you declare them. If you
declared pAge as previously described, and you wanted pAge to point to age, you would have
to explicitly assign pAge to the address of age. The following statement does this:
What value is now in pAge? You do not know exactly, but you know it is the address of age,
wherever that is. Instead of assigning the address of age to pAge with an assignment operator,
you can declare and initialize pointers at the same time. These lines declare and initialize both
age and pAge:
The dereference operator produces a value that tells the pointer where to point. Without the *,
the last printf() would print an address (the address of age). With the *, the printf()
prints the value at that address.
You can assign a different value to age with the following statement:
*pAge=41;
TIP
The * appears before a pointer variable in only two placeswhen you declare a
pointer variable, and when you dereference a pointer variable (to find the data it
points to).
Program
#include <stdio.h>
main ()
{
int i; //A
int * ia; //B
i = 10; //C
ia = &i; //D
Explanation
1. The program declares two variables, so memory is allocated for two variables. i is of the
type of int, and ia can store the address of an integer, so it is a pointer to an integer.
5. When you execute ia = &i then the address and value are assigned to i, thus i has the
address of 4000 and value is 1000.
6. You can print the value of i by using the format %8u because addresses are usually in the
format unsigned long, as given in statement E.
7. Statement F prints the value of i, (at the location 1000).
8. Alternatively, you can print the value at location 1000 using statement G. *ia means you
are printing the value at the location specified by ia. Since I has the value for 1000, it
will print the value at location 1000.
9. When you execute *ia = 50, which is specified by statement H, the value 50 is written
at the location by ia. Since ia specifies the location 1000, the value at the location 1000
is written as 50.
10. Since i also has the location 1000, the value of i gets changed automatically from 10 to
50, which is confirmed from the printf statement written at position i.
Points to Remember
2. You can define a pointer by including a* before the name of the variable.
3. You can get the address where a variable is stored by using &.
EXERCISE:
1. If i is a variable and p points to i. which of the following expressions are aliases for i?
(a) *p (c) *&p (e) *i (g) *&i
(b) &p (d) &*p (f) &i (h) &*i
2. Explain the difference between &p, p, and *p all have different meanings
MORE ASSIGNMENT
int *p;
int* p;
int * p;
The character * can appear anywhere between the data type name and the variable
name.
Now, consider the following statement:
int* p, q;
In this statement, only p is the pointer variable, not q. Here, q is an int variable. To
avoid
confusion, we prefer to attach the character * to the variable name. So the
preceding
statement is written as:
int *p, q;
EXAMPLE 1:
int *p;
int x;
Suppose that we have the memory allocation for p and x as shown in Figure 1
Suppose that the following statements are executed in the order given:
x = 50;
p = &x;
*p = 38;
The values of &p, p, *p, &x, and x are shown after each of these statements
executes.
After the statement x = 50; executes, the values of &p, p, *p, &x, and x are as follows:
After the statement p = &x; executes, the values of &p, p, *p, &x, and x are as follows:
After the statement *p = 38; executes, the values of &p, p, *p, &x, and x are as
follows.
(Because *p and x refer to the same memory space, the value of x is also changed to
38.)
EXAMPLE 1:
#include <stdio.h>
#include <conio.h>
int main()
int *p;
int x = 37;
p = &x; //Line 2
*p = 58; //Line 4
getch();
return 0;
struct studentType
{
char name[26];
double gpa;
int sID;
char grade;
};
studentType student;
studentType *studentPtr
student is an object of type studentType, and studentPtr is a pointer variable of type studentType.
studentPtr = &student;
The following statement stores 3.9 in the component gpa of the object student:
(*studentPtr).gpa = 3.9;
Lets examine this a bit. In the expression (*studentPtr).gpa, the operator * evaluates first, so the expression
*studentPtr evaluates first.
Because studentPtr is a pointer variable of type studentType, *studentPtr refers to a memory space of type
studentType, which is a struct. Therefore, (*studentPtr).gpa refers to the component gpa of that struct.
Consider the expression *studentPtr.gpa. Let us see how this expression gets evaluated. Because . (dot) has a
higher precedence than *, the expression studentPtr.gpa evaluates first. The expression studentPtr.gpa would
result in a syntax error, as studentPtr is not a struct variable, so it has no such component as gpa.
As you can see, in the expression (*studentPtr).gpa, the parentheses are important. However, typos can be
problematic. Therefore, to simplify the accessing of class or struct components via a pointer, C++ provides
another operator called the member access operator arrow, . The operator consists of two
consecutive symbols: a hyphen - and the greater than > sign.
The syntax for accessing a class (struct) member using the operator -> is:
pointerVariableNameclassMemberName
(*studentPtr).gpa = 3.9;
studentPtrgpa = 3.9;
Note:
Pointer variables must be initialized if you do not want them to point to anything. Pointer
variables are initialized using the constant value 0, called the null pointer. Thus, the statement p =
0; stores the null pointer in p, that is, p points to nothing. Some programmers use the named
constant NULL to initialize pointer variables. The following two statements are equivalent:
p = NULL;
p = 0;
The number 0 is the only number that can be directly assigned to a pointer variable.
Purpose of Pointers
Variables that are created during program execution are called dynamic variables. With
the help of pointers, C++ creates dynamic variables. C++ provides two operators, new
and delete, to create and destroy dynamic variables, respectively. When a program requires a new
variable, the operator new is used. When a program no longer needs a dynamic variable, the
operator delete is used.
Operator new
The operator new has two forms: one to allocate a single variable and another to allocate
an array of variables. The syntax to use the operator new is:
The operator new allocates memory (a variable) of the designated type and returns a pointer to itthat is,
the address of this allocated memory. Moreover, the allocated memory is uninitialized.
Consider the following declaration:
int *p;
char *q;
int x;
The statement:
p = &x;
stores the address of x in p. However, no new memory is allocated. On the other hand,
consider the following statement:
p = new int;
This statement creates a variable during program execution somewhere in memory and stores the address
of the allocated memory in p. The allocated memory is accessed via pointer dereferencingnamely, *p.
Similarly, the statement:
q = new char[16];
creates an array of 16 components of type char and stores the base address of the array in q.
Because a dynamic variable is unnamed, it cannot be accessed directly. It is accessed indirectly by the
pointer returned by new. The following statements illustrate this concept:
Operator delete
Suppose you have the following declaration:
int *p;
This statement declares p to be a pointer variable of type int. Next, consider the following statements:
p = new int; //Line 1
*p = 54; //Line 2
p = new int; //Line 3
*p = 73; //Line 4
The statement in Line 2 stores 54 into the memory space that p points to, which is 1500 (see Figure 5(b)).
Next, the statement in Line 3 executes, which allocates a memory space of type int and stores the address
of the allocated memory space into p. Suppose the address of this allocated memory space is 1800. It
follows that the value of p is now 1800 (see Figure 5(c)). The statement in Line 4 stores 73 into the
memory space that p points to, which is 1800. In other words, after the execution of the statement in Line
4, the value stored into memory space at location 1800 is 73 (see Figure 5(d)).
Now the obvious question is what happened to the memory space 1500 that p was pointing to after
execution of the statement in Line 1. After execution of the statement in Line 3, p points to the new
memory space at location 1800.
The previous memory space at location 1500 is now inaccessible. In addition, the memory space 1500
remains as marked allocated. In other words, it cannot be reallocated. This is called memory leak. That
is, there is an unused memory space that cannot be allocated.
Imagine what would happen if you executed statements, such as Line 3, a few thousand or a few million
times. There would be a good amount of memory leak. The program might then run out of memory
spaces for data manipulation and eventually result in an abnormal termination of the program.
The question at hand is how to avoid memory leak. When a dynamic variable is no longer needed, it can
be destroyed; that is, its memory can be deallocated.
The C++ operator delete is used to destroy dynamic variables. The syntax to use the operator
delete has two forms:
delete p;
delete [] name;
delete str;
deallocate the memory spaces that the pointers p, name, and str point to.
Suppose p and name are pointer variables, as declared previously. Notice that an expression
such as:
delete p;
or:
delete [] name;
only marks the memory spaces that these pointer variables point to as deallocated. Depending on a
particular system, after these statements execute, these pointer variables may still contain the addresses of
the deallocated memory spaces. In this case, we say that these pointers are dangling. Therefore, if later
you access the memory spaces via these pointers without properly initializing them, depending on a
particular system, either the program will access a wrong memory space, which may result in corrupting
data, or the program will terminate with an error message.
One way to avoid this pitfall is to set these pointers to NULL after the delete operation. Also note that for
the operator delete to work properly, the pointer must point to a valid memory space.
The operations that are allowed on pointer variables are the assignment and relational operations
and some limited arithmetic operations. The value of one pointer variable can be assigned to another
pointer variable of the same type. Two pointer variables of the same type can be compared for equality,
and so on. Integer values can be added and subtracted from a pointer variable. The value of one pointer
variable can be subtracted from another pointer variable.
copies the value of q into p. After this statement executes, both p and q point to the same memory location.
Any changes made to *p automatically change the value of *q, and vice versa.
The expression:
p == q
evaluates to true if p and q have the same valuethat is, if they point to the same
memory location. Similarly, the expression:
p != q
int *p;
double *q;
char *chPtr;
studentType *stdPtr; //studentType is as defined before
Recall that the size of the memory allocated for an int variable is 4 bytes, a double
variable is 8 bytes, and a char variable is 1 byte. The memory allocated for a variable of type studentType is
then 40 bytes.
The statement:
p++; or p = p + 1;
increments the value of p by 4 bytes because p is a pointer of type int. Similarly, the statements:
q++;
chPtr++;
increment the value of q by 8 bytes and the value of chPtr by 1 byte, respectively. The statement:
stdPtr++;
The increment operator increments the value of a pointer variable by the size of the memory to which it is
pointing. Similarly, the decrement operator decrements the value of a pointer variable by the size of the
memory to which it is pointing.
p = p + 2;
Thus, when an integer is added to a pointer variable, the value of the pointer variable is incremented by
the integer times the size of the memory that the pointer is pointing to.
Similarly, when an integer is subtracted from a pointer variable, the value of the pointer variable
is decremented by the integer times the size of the memory to which the pointer is pointing.
Note:
Pointer arithmetic can be very dangerous. Using pointer arithmetic, the program can accidentally access
the memory locations of other variables and change their content without warning, leaving the
programmer trying to find out what went wrong. If a pointer variable tries to access either the memory
spaces of other variables or an illegal memory space, some systems might terminate the program with an
appropriate error message. Always exercise extra care when doing pointer arithmetic.
ARRAYS
An array created during the execution of a program is called a dynamic array. To create a dynamic array,
we use the second form of the new operator.
The statement:
int *p;
p = new int[10];
allocates 10 contiguous memory locations, each of type int, and stores the address of the
first memory location into p. In other words, the operator new creates an array of 10 components
of type int, it returns the base address of the array, and the assignment operator stores the base
address of the array into p. Thus, the statement:
*p = 25;
store 35 into the second memory location. Thus, by using the increment and decrement operations, you
can access the components of the array. Of course, after performing a few increment operations, it is
possible to lose track of the first array component. C++ allows us to use array notation to access these
memory locations. For example, the statements:
p[0] = 25;
p[1] = 35;
store 25 and 35 into the first and second array components, respectively. That is, p[0] refers to the first
array component, p[1] refers to the second array component, and so on.
In general, p[i] refers to the (i + 1)th array component. After the preceding statements execute, p still points
to the first array component. Moreover, the following for loop initializes each array component to 0:
NOTE:
The statement:
int list[5];
declares list to be an array of five components. Recall that list itself is a variable, and the value stored in list is the
base address of the arraythat is, the address of the first array component. Suppose the address of the first array
component is 1000. Figure 6 shows list and the array list.
Because the value of list, which is 1000, is a memory address, list is a pointer variable. However, the
value stored in list, which is 1000, cannot be altered during program execution. That is, the value of list is
constant. Therefore, the increment and decrement operations cannot be applied to list. In fact, any attempt
to use the increment or decrement operations on list results in a compile-time error.
Notice that here, we are only saying that the value of list cannot be changed. However, the data into the
array list can be manipulated as before. For example, the statement list[0] = 25; stores 25 into the first
array component. Similarly, the statement
list[3] = 78; stores 78 into the fourth component of list (see Figure 7).
p = list;
copies the value of list, which is 1000, the base address of the array, into p. We are allowed to perform
increment and decrement operations on p.
Fig.1
Linked list: A list of items, called nodes, in which the order of the nodes is determined
by the address, called the link, stored in each node.
The list in Figure 2 is an example of a linked list.
Fig. 2
The arrow in each node indicates that the address of the node to which it is pointing is
stored in that node. The down arrow in the last node indicates that this link field is NULL.
For a better understanding of this notation, suppose that the first node is at memory location
1200, and the second node is at memory location 1575. We thus have Figure 3.
The value of the head is 1200, the data part of the first node is 45, and the link
component
of the first node contains 1575, the address of the second node. If no confusion
arises, then
we will use the arrow notation whenever we draw the figure of a linked list.
Because each node of a linked list has two components, we need to declare each node as a
class or struct. The data type of each node depends on the specific applicationthat is, what kind
of data is being processed. However, the link component of each node is a pointer. The data type
of this pointer variable is the node type itself. For the previous linked list, the definition of the
node is as follows. (Suppose that the data type is int.)
struct nodeType
{
int info;
nodeType *link;
};
nodeType *head;
This linked list has four nodes. The address of the first node is stored in the pointer head.
Each node has two components: info, to store the info, and link, to store the address of
the next node. For simplicity, we assume that info is of type int.
Suppose that the first node is at location 2000, the second node is at location 2800, the third node
is at location 1500, and the fourth node is at location 3600. Therefore, the value of head is 2000, the
value of the component link of the first node is 2800, the value of the component link of the second
node is 1500, and so on. Also, the value 0 in the component link of the last node means that this
value is NULL, which we indicate by drawing a down arrow. The number at the top of each
node is the address of that node.
The following table shows the values of head and some other nodes in the list shown
in Figure 4.
Suppose that current is a pointer of the same type as the pointer head. Then, the statement:
current = head;
This statement copies the value of currentlink, which is 2800, into current.
Therefore, after this statement executes, current points to the second node in the
list. (When working with linked lists, we typically use these types of statements
to advance a pointer to the next node in the list.) See Figure 6.
Fig. 6
Note:
Value
Headlinklink 1500
headlinklinkinfo 63
headlinklinklink 3600
headlinklinklinkinfo 45
currentlinklink 3600
currentlinklinkinfo 45
currentlinklinklink 0 (that is, NULL)
currentlinklinklinkinfo Does not exist
These operations require the list to be traversed. That is, given a pointer
to the first node of the list, we must step through the nodes of the list.
Suppose that the pointer head points to the first node in the list, and the link of
the lastnode is NULL. We cannot use the pointer head to traverse the list because if we use
head to traverse the list, we would lose the nodes of the list. This problem occurs because
the links are in only one direction. The pointer head contains the address of the first
node, the first node contains the address of the second node, the second node contains the
address of the third node, and so on. If we move head to the second node, the first node
is lost (unless we save a pointer to this node).
If we keep advancing head to the next node, we will lose all of the nodes of the
list(unless we save a pointer to each node before advancing head, which is impractical because
it would require additional computer time and memory space to maintain the list).
Therefore, we always want head to point to the first node. It now follows that we must
traverse the list using another pointer of the same type. Suppose that current is a pointer
of the same type as head. The following code traverses the list:
current = head;
For example, suppose that head points to a linked list of numbers. The following
code outputs the data stored in each node:
current = head;
while (current != NULL)
{
cout << currentinfo << " ";
current = currentlink;
}
Building a Linked List
Let us see how to build a linked list. First, we consider a linked list in general. If the data we read
is unsorted, the linked list will be unsorted. Such a list can be built in two ways: forward and
backward. In the forward manner, a new node is always inserted at the end of the linked list. In
the backward manner, a new node is always inserted at the beginning of the list. We will
consider both cases.
struct nodeType
{
int info;
nodeType *link;
};
Suppose that the nodes are in the usual info-link form, and info is of type int. Let us assume that we
process the following data:
2 15 8 24 34
We need three pointers to build the list: one to point to the first node in the list, which
cannot be moved; one to point to the last node in the list; and one to create the new node.
Consider the following variable declaration:
int num;
Suppose that first points to the first node in the list. Initially, the list is empty, so
both first and last are NULL. Thus, we must have the statements:
first = NULL;
last = NULL;
After statement 1 executes, num is 2. Statement 2 creates a node and stores the address of
that node in newNode. (fig 13(a) )
Statement 3 stores 2 in the info field of newNode, and statement 4 stores NULL in the link field of
newNode (see Figure 13 (b) ).
Because first is NULL, we execute statements 5a and 5b. Figure 14 shows the resulting list.
5a first = newNode;
5b last = newNode;
Fig 14
We now repeat statements 1 through 6b. After statement 1 executes, num is 15. Statement
2 creates a node and stores the address of this node in newNode. Statement 3 stores 15 in
the info field of newNode, and statement 4 stores NULL in the link field of newNode (see
Figure 15).
Fig. 15
Because first is not NULL, we execute statements 6a and 6b. Figure 16 shows the resulting list.
We now repeat statements 1 through 6b three more times. Figure 17 shows the resulting list.
Fig 17
To build the linked list, we can put the previous statements in a loop and execute the loop until
certain conditions are met. We can, in fact, write a C++ function to build a linked list.
Suppose that we read a list of integers ending with -999 (sentinel value). The following function,
buildListForward, builds a linked list (in a forward manner) and returns the pointer of the built list.
nodeType * buildListForward( )
{
nodeType *first, *newNode, *last;
int num;
cout << "Enter a list of integers ending with -999." << endl;
cin >> num;
first = NULL;
while (num != -999)
{
newNode = new nodeType;
newNodeinfo = num;
newNodelink = NULL;
if (first == NULL)
{
first = newNode;
last = newNode;
}
else
{
Lastlink = newNode;
last = newNode;
}
cin >> num;
} //end while
return first;
} //end buildListForward
Equivalent C:
In computing, malloc is a subroutine for performing dynamic memory allocation in the C and
C++ programming languages, though its use in C++ has been largely superseded by operators
new and new[]. malloc is part of the standard library for both languages and is declared in the
stdlib.h header although it is also declared within the std namespace via C++'s cstdlib
header.
Many implementations of malloc are available, each of which performs differently depending on
the computing hardware and how a program is written. Performance varies in both execution
time and required memory. The pointer to memory allocated using malloc must eventually be
passed to the free subroutine to deallocate the memory in order to avoid memory leaks.
The malloc function is one of the functions in standard C to allocate memory. Its function
prototype is
which allocates size bytes of memory. If the allocation succeeds, a pointer to the block of
memory is returned. This pointer is guaranteed to be suitably aligned to any type (including
struct and such), otherwise a NULL pointer is returned.
Memory allocated via malloc is persistent: it will continue to exist, even after the program
leaves the scope which called the allocation, until the program terminates or the memory is
explicitly deallocated by the programmer. This is achieved by use of the free subroutine. Its
prototype is
which releases the block of memory pointed to by pointer. pointer must have been previously
returned by malloc, calloc, or realloc and pointer must not be used after it has been passed
to free. In particular memory allocated via new or new[] should not be passed to free, and
pointers which did not come from a malloc (e.g. stack variables) or which have already been
freed must not be sent to free. It is safe to call free on a NULL pointer, which has no effect.
Usage example
The standard method of creating an array of 10 int objects:
int array[10];
However, if one wishes to allocate a similar array dynamically, the following code could be used:
if (ptr == NULL) {
/* Memory could not be allocated, the program should
handle the error here as appropriate. */
} else {
/* Allocation succeeded. Do something. */
free(ptr); /* We are done with the int objects, and
free the associated pointer. */
ptr = NULL; /* The pointed-to-data must not be used again,
unless re-assigned by using malloc
again. */
}
Program
Here is a program for building and printing the elements of the linked list:
# include <stdio.h>
# include <stdlib.h>
struct node
{
int data;
struct node *link;
};
if(p==NULL)
{
printf("Error\n");
exit(0);
}
pdata = n;
plink = p; /* makes the pointer pointing to itself because it
is a circular list*/
}
else
{
temp = p;
/* traverses the existing list to get the pointer to the last node of
it */
while (templink != p)
temp = templink; //note only one line executed here-while loop
if(templink == NULL)
{
printf("Error\n");
exit(0);
}
temp = templink;
tempdata = n;
templink = p;
}
return (p);
}
int main()
{
int n;
int x;
struct node *start = NULL ;
printf("Enter the nodes to be created \n");
scanf("%d",&n);
Explanation
1. This program uses a strategy of inserting a node in an existing list to get the list created.
An insert function is used for this.
2. The insert function takes a pointer to an existing list as the first parameter, and a data
value with which the new node is to be created as a second parameter, creates a new node
by using the data value, appends it to the end of the list, and returns a pointer to the first
node of the list.
3. Initially the list is empty, so the pointer to the starting node is NULL. Therefore, when
insert is called first time, the new node created by the insert becomes the start node.
4. Subsequently, the insert traverses the list to get the pointer to the last node of the existing
list, and puts the address of the newly created node in the link field of the last node,
thereby appending the new node to the existing list.
5. The main function reads the value of the number of nodes in the list. Calls iterate that
many times by going in a while loop to create the links with the specified number of
nodes.
Points to Remember
1. Linked lists are used when the quantity of data is not known prior to execution.
2. In linked lists, data is stored in the form of nodes and at runtime, memory is allocated for
creating nodes.
3. Due to overhead in memory allocation and deallocation, the speed of the program is
lower.
PROGRAMS
Introduction
A linked list is a recursive data structure. A recursive data structure is a data structure that has
the same form regardless of the size of the data. You can easily write recursive programs for such
data structures.
Program
# include <stdio.h>
# include <stdlib.h>
struct node
{
int data;
struct node *link;
};
if(p==NULL)
{
p=(struct node *)malloc(sizeof(struct node));
if(p==NULL)
{
printf("Error\n");
exit(0);
}
pdata = n;
plink = NULL;
}
else
plink = insert(plink, n);/* the while loop replaced by
recursive call */
return (p);
}
Explanation
1. This recursive version also uses a strategy of inserting a node in an existing list to create
the list.
2. An insert function is used to create the list. The insert function takes a pointer to an
existing list as the first parameter, and a data value with which the new node is to be
created as the second parameter. It creates the new node by using the data value, then
appends it to the end of the list. It then returns a pointer to the first node of the list.
3. Initially, the list is empty, so the pointer to the starting node is NULL. Therefore, when
insert is called the first time, the new node created by the insert function becomes the
start node.
4. Subsequently, the insert function traverses the list by recursively calling itself.
5. The recursion terminates when it creates a new node with the supplied data value and
appends it to the end of the list.
Points to Remember
1. A linked list has a recursive data structure.
INSERTION
Consider the linked list shown in Figure 18.
Figure 18
Suppose that p points to the node with info 65, and a new node with info 50 is to be created and
inserted after p. Consider the following statements:
newNode = new nodeType; //create newNode
newNodeinfo = 50; //store 50 in the new node
newNodelink = plink;
plink = newNode;
plink = newNode;
newNodelink = plink;
Figure 19
From Figure 19, it is clear that newNode points back to itself and the remainder of the
list is lost.
Using two pointers, we can simplify the insertion code somewhat. Suppose q points to
the node with info 34 (see Figure 20).
Figure 20
newNodelink = q;
plink = newNode;
The order in which these statements execute does not matter. To illustrate this, suppose
that we execute the statements in the following order:
plink = newNode;
newNodelink = q;
Table 2
DELETION
Consider the linked list shown in Figure 21.
Figure 21
Suppose that the node with info 34 is to be deleted from the list. The following
statement removes the node from the list.
plink = plinklink;
Figure 22 shows the resulting list after the preceding statement executes.
Figure 22
From Figure 22, it is clear that the node with info 34 is removed from the list.
However, the memory is still occupied by this node, and this memory is inaccessible; that
is, this node is dangling. To deallocate the memory, we need a pointer to this node. The
following statements delete the node from the list and deallocate the memory occupied
by this node.
q = plink;
plink = qlink;
delete q;