Вы находитесь на странице: 1из 38

1.

Address and Pointers


Introduction

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

printf ("Value of k is %d\n", k);


}

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.

The address of the first byte is said to be the address of the


variable. In the following figure, the variable i occupies the
bytes at addresses 2000 and 2001, so i's address
is 2000:

Instead of showing addresses as numbers in our examples, we


will use a simpler notation. To indicate that a pointer variable
p stores the address of a variable i. We will show the contents
of p as an arrow directed toward i:
pointer variables. When we store the address of a variable i in
the pointer variable p. we say that p "points to" i. In other
words, a pointer is nothing more than an
address, and a pointer variable is just a variable that can store
an address.

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.

There are two pointer operators in C:

& The "address of" operator

The dereferencing (or indirection) operator

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:

int age=30; // Declare a variable to hold my age

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

To declare the pAge pointer variable, you must do the following:

int * pAge; // Declare an integer pointer

Similar to the declaration for age, this declaration reserves a

variable called pAge. pAge is not a normal integer variable,

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:

int *pAge; // Declare an integer pointer


Either method is okay, but you must remember that the is not part of the name. When you later
use pAge, you will not always prefix the name with the , unless you are dereferencing it at the
time (as we will see).
Other declarations:

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.

Assigning Values to Pointers

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

float *point; // Declares a floating-point pointer

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:

pAge = &age; // Assign the address of age to pAge

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:

int age=30; /* Declares a regular integer


variable, putting 30 in it */

int *pAge=&age; /* Declares an integer pointer,


initializing it with the address
of pAge */

These two lines produce the variables described in Figure 1.


If you wanted to print the value of age, you could do so with the following printf():

printf("%d", age); // Prints the value of age

You also can print the value


of age like this:

printf("%d", *pAge); // Dereferences 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:

age=41; /* Assign a new value to age */

You also can assign a value to age like this:

*pAge=41;

This declaration assigns 41 to the value to which pAge points.

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

printf (" The address of i is %8u \n", ia); //E


printf (" The value at that location is %d\n", i); //F
printf (" The value at that location is %d\n", *ia); //G

*ia = 50; //H


printf ("The value of i is %d\n", i); //I
}

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.

2. The memory allocation is as follows:

3. i gets the address 1000, and ia gets address 4000.

4. When you execute i = 10, 10 is written at location 1000.

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

1. Pointers give a facility to access the value of a variable indirectly.

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

Note the following observations. The statement:

int *p;

is equivalent to the statement:

int* p;

which is equivalent to the statement:

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;

Of course, the statement:

int *p, *q;

declares both p and q to be pointer variables of type int.

EXAMPLE 1:

Consider the following statements:

int *p;
int x;

Suppose that we have the memory allocation for p and x as shown in Figure 1

The values of &p, p, *p, &x, and x are as follows:

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:

//Chapter 14: Example 14-3

#include <stdio.h>

#include <conio.h>

int main()

int *p;

int x = 37;

printf("line 1: x = %d\n", x); //Line 1

p = &x; //Line 2

printf("line 3: *p =%d\n" ,*p,", x = %d\n", x); //Line 3

*p = 58; //Line 4

printf("line 5: *p = %d\n",* p,", x =%d\n", x); //Line 5

printf("line 6: Address of p = %8u\n",&p); //Line 6

printf("line 7: Value of p = %8u\n", p); //Line 7

printf("line 8: Value of the memory location pointed to by *p =%d\n", *p); //Line 8

printf("line 9: Address of x = %8u\n", &x); //Line 9

printf("line 10: x = %d\n", x); //Line 10

getch();
return 0;

Structs and Pointer Variables


We have learned how to declare and manipulate pointers of simple data types, such as int and char. You
can also declare pointers to other data types, such as structures and classes. You will now learn how to
declare and manipulate pointers to classes and structs. (Recall that both classes and structs have the same
capabilities. The only difference between classes and structs is that, by default, all members of a class are
private, and, by default, all members of a struct are public. The following discussion applies
to both.)

Consider the following declaration of a struct:

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.

The following statement stores the address of student in studentPtr:

studentPtr = &student;

The following statement stores 3.9 in the component gpa of the object student:

(*studentPtr).gpa = 3.9;

The expression (*studentPtr).gpa is a mixture of pointer dereferencing and the class


component selection. In C or C++, the dot operator, . , has a higher precedence than the
dereferencing operator.

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

Thus, the statement:

(*studentPtr).gpa = 3.9;

is equivalent to the statement:

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.

In C++, new and delete are reserved words.

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:

new dataType; //to allocate a single variable


new dataType[intExp]; //to allocate an array of variables

in which intExp is any expression evaluating to a positive integer.

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:

int *p; //p is a pointer of type int


char *name; //name is a pointer of type char
string *str; //str is a pointer of type string

p = new int; //allocates memory of type int


//and stores the address of the
//allocated memory in p
*p = 28; //stores 28 in the allocated memory

name = new char[5]; //allocates memory for an array of


//five components of type char and
//stores the base address of the array
//in name
strcpy(name, "John"); //stores John in name

str = new string; //allocates memory of type string


//and stores the address of the
//allocated memory in str
*str = "Sunny Day"; //stores the string "Sunny Day"in
//the memory pointed to by str

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

Figure 5 shows the effect of these statements.


The number 1500 on top of the box indicates the address of the memory space. The statement in Line 1
allocates memory space of type int and stores the address of the allocated memory space into p. Suppose
that the address of allocated memory space is 1500. Then, the value of p after the execution of this
statement is 1500 (see Figure 5(a)).

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 pointerVariable; //to deallocate a single dynamic variable


delete [] pointerVariable; //to deallocate a dynamically created array

Thus, given the declarations of the previous section, the statements:

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.

For example, suppose that we have the following statements:

int *p, *q;


The statement:
p = q;

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

evaluates to true if p and q point to different memory locations.


The arithmetic operations that are allowed differ from the arithmetic operations on numbers. First, let us
use the following statements to explain the increment and decrement operations on pointer variables:

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++;

increments the value of stdPtr by 40 bytes.

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.

Moreover, the statement:

p = p + 2;

increments the value of p by 8 bytes.

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;

declares p to be a pointer variable of type int. The statement:

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;

stores 25 into the first memory location, and the statements:


p++; //p points to the next array component
*p = 35;

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:

for (j = 0; j < 10; j++)


p[j] = 0;

in which j is an int variable.

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

If p is a pointer variable of type int, then the statement:

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.

An array name is a constant pointer.


LINK LISTS
A linked list is a collection of components called nodes. Every node (except the last node)
contains the address of the next node. Thus, every node in a linked list has two components:
one to store the relevant information (that is, data) and one to store the address, called the
link, of the next node in the list. The address of the first node in the list is stored in a separate
location called the head or first.

Fig.1

Figure 1 is a pictorial representation of a node.

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;
};

The variable declaration is:

nodeType *head;

Consider the following link list:

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;

copies the value of head into current (see Figure 5).


Llosmthere is one thing that Id love more than anything in this world
Fig 5

Now consider the statement:

current = current link;

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

TRAVERSING A LINKED LIST


The basic operations of a linked list are as follows: search the list to determine
whether aparticular item is in the list, insert an item in the list, and delete an item from
the list.

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;

while (current != NULL)


{
//Process the current node
current = currentlink;
}

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;
};

BUILDING A LINKED LIST FORWARD

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:

NodeType *first, *last, *newNode;

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;

to initialize first and last to NULL.

Next, consider the following statements:

1 cin >> num; //read and store a number in num


2 newNode = new nodeType; //allocate memory of type nodeType
//and store the address of the
//allocated memory in newNode
3 newNodeinfo = num; //copy the value of num into the
//info field of newNode

4 newNodelink = NULL; //initialize the link field of


//newNode to NULL

5 if (first == NULL) //if first is NULL, the list is empty;


//make first and last point to newNode
{
5a first = newNode;
5b last = newNode;
}
6 else //list is not empty
{
6a lastlink = newNode; //insert newNode at the end of the list
6b last = newNode; //set last so that it points to the
//actual last node in the list
}
Let us now execute these statements. Initially, both first and last are NULL.
Therefore, we have the list as shown in Figure 12.

Fig 12 empty list

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

(a) newNode = new nodeType (b) newNodeinfo = num;


newNodelink = NULL;

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.

Fig 16 6a lastlink = newNode;


6b last = newNode;

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

void *malloc(size_t size);

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

void free(void *pointer);

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:

/* Allocate space for an array with ten elements of type


int. */
int *ptr = (int *) malloc(10 * sizeof (int));

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;
};

struct node *insert(struct node *p, int n)


{
struct node *temp;
/* if the existing list is empty then insert a new node as the
starting node */
if(p==NULL)
{
p=(struct node *)malloc(sizeof(struct node)); /* creates new node
data value passesas parameter */

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

templink = (struct node *)malloc(sizeof(struct node)); /*


creates new node using data value passes as parameter and puts its
address in the link field of last node of the existing list*/

if(templink == NULL)
{
printf("Error\n");
exit(0);
}
temp = templink;
tempdata = n;
templink = p;
}
return (p);
}

void printlist ( struct node *p )


{
struct node *temp;
temp = p;
printf("The data values in the list are\n");
if(p != NULL)
{
do
{
printf("%d\t",temp->data);
temp = templink;
}while (temp!= p);
}
else
printf("The list is empty\n");
}

int main()
{
int n;
int x;
struct node *start = NULL ;
printf("Enter the nodes to be created \n");
scanf("%d",&n);

while ( n-- > 0 )


{
printf( "Enter the data values to be placed in a node\n");
scanf("%d",&x);
start = insert ( start, x );
}
printf("The created list is\n");
printlist ( start );
return 0;
}

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.

4. The data is accessed using the starting pointer of the list.

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;
};

struct node *insert(struct node *p, int n)


{
struct node *temp;

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);
}

void printlist ( struct node *p )


{
printf("The data values in the list are\n");
while (p!= NULL)
{
printf("%d\t",pdata);
p = plink;
}
}

int void main()


{
int n;
int x;
struct node *start = NULL ;

printf("Enter the nodes to be created \n");


scanf("%d",&n);

while ( n-- > 0 )


{
printf( "Enter the data values to be placed in a node\n");
scanf("%d",&x);
start = insert ( start, x );
}

printf("The created list is\n");


printlist ( start );
return 0;
}

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.

2. Writing recursive programs for such structures is programmatically convenient.

ITEM INSERTION AND DELETION


This section discusses how to insert an item into, and delete an item from, a linked list.
Consider the following definition of a node. (For simplicity, we assume that the info
type is int. The next section, which discusses linked lists (which is an abstract data type (ADT))
uses the generic definition of a node.)
struct nodeType
{
int info;
nodeType *link;
};

We will use the following variable declaration:


nodeType *head, *p, *q, *newNode;

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;

Table 1 shows the effect of these statements.


Note that the sequence of statements to insert the node is very important because to insert
newNode in the list, we use only one pointer, p, to adjust the links of the node of the linked list.
Suppose that we reverse the sequence of the statements and execute the statements in the
following order:

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

The following statements insert newNode between p and q.

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;

Table 3 shows the effect of these statements.

Вам также может понравиться