|
|
Summary
6) Introduction
7) Relationship between Pointers and Arrays
8) Pointer Expressions and Arithmetic
9) Pointers Comparison
10) Pointer, String and Arrays
11) Tips
Introduction
In the previous lecture, we had just started the discussion on the topic of pointers.
This
topic is little complicated, yet the power we get with the pointers is very interesting.
We
can do many interesting things with pointers. When other languages like Java evolve
with
the passage of time, pointers are explicitly excluded. In today’s lecture, we will
discuss
pointers, the relationship between pointers and arrays, pointer expressions, arithmetic
with pointers, relationship between arrays and pointer, strings etc.
Relationship between Pointers and Arrays
When we write int x,
it means that we have attached a symbolic name
x, at some memory
location. Now we can use x
= 10 which replaces the value at that memory location with
10. Similarly while talking about arrays, suppose an array as
int y[10]. This means that
we have reserved memory spaces for ten integers and named it collectively as
y. Now we
will see what actually y is? 'y'
represents the memory address of the beginning of this
collective memory space. The first element of the array can be accessed as
y[0].
Remember arrays index starts from 0 in C language, so the memory address of first
element i.e. y[0]
is stored in y.
“The name of the array is a constant pointer which contains the memory address
of the first element of the array”
Page 161
The difference between this and an ordinary pointer is that the array name is a
constant
pointer. It means that the array name will always point to the start of the array.
In other
words, it always contains the memory address of the first element of the array and
cannot
be reassigned any other address. Let's elaborate the point with the help of following
example.
int y[10];
int *yptr;
In the above statements, we declare an array
y of ten integers and a
pointer to an integer
i.e. yptr. This
pointer may contain a memory address of an integer.
yptr = y;
This is an assignment statement. The value of
y i.e. the address of the
first element of the
array is assigned to yptr.
Now we have two things pointing to the same place,
y and
yptr.
Both are pointing to the first element of the array. However,
y is a constant pointer
and
always points to the same location whereas
yptr is a pointer variable
that can also point to
any other memory address.
Pointer Expressions and Arithmetic
Suppose we have an array y
and yptr,
a pointer to array. We can manipulate arrays with
both y and
yptr. To access
the fourth element of the array using
y, we can say
y[3]; with
yptr, we can write as
*(yptr + 4). Now we have
to see what happens when we increment
or add something to a pointer. We know that
y is a constant pointer
and it can not be
incremented. We
can write y[0], y[1]
etc. On the other hand,
yptr is a pointer variable
and can be written as the statement
yptr = y. It means that
yptr contains the
address of the
first element of the array. However, when we say
yptr++, the value of
yptr is
incremented. But how much? To explain it further, we increment a normal integer
variable like x++. If x
contains 10, it will be incremented by 1 and become 11. The
increment of a pointer depends on its data type. The data type, the pointer points
to,
determines the amount of increment. In this case,
yptr is an integer pointer.
Therefore,
when we increment the yptr,
it points to the next integer in the memory. If an integer
occupies four bytes in the memory, then the
yptr++; will increment
its value by four.
This can be understood from the following example.
// This program will print the memory address of a pointer and its incremented address.
#include<iostream.h>
main()
{
int y[10]; // an array of 10 integers
int *yptr; // an integer pointer
yptr = y; // assigning the start of array address to pointer
Page 162
// printing the memory address
cout << “The memory address of yptr = “ << yptr << endl ;
yptr++; // incrementing the pointer
// printing the incremented memory address
cout << “The memory address after incrementing yptr = ” << yptr << endl;
}
In the above program, the statement
cout << yptr will show
the memory address the yptr
points to. You will notice the difference between the two printed addresses. By
default,
the memory address is printed in hexadecimal by the C output system. Therefore,
the
printed address will be in hexadecimal notation. The difference between the two
addresses will be four as integer occupies four bytes and
yptr is a pointer to an
integer.
“When a pointer is incremented, it actually jumps the number of memory spaces
according to the data type that it points to”
The sample out put of the program is:
The memory address of yptr = 0x22ff50
The memory address after incrementing yptr = 0x22ff54
yptr which was pointing to the start of the array
y, starts pointing to the
next integer in
memory after incrementing it. In other words, yptr
is pointing to the 2nd element of the
array. On being incremented again, the
yptr will be pointing to
the next element of the
array i.e. y[2],
and so on. We know that & is address operator which can be used to get
the memory address. Therefore, we can also get the address of the first element
of the
array in yptr as:
yptr = &y[0] ;
y[0] is a single element and its address can be got with the use of. the address
operator
(&). Similarly we can get the address of 2nd or 3rd
element as &y[1], &y[2]
respectfully.
We can get the address of any array element and assign it to
yptr.
Suppose the yptr
is pointing to the first element of the array
y. What will happen if
we
increment it too much? Say, the array size is 10. Can we increment the
yptr up to 12
times? And what will happen? Obviously, we can increment it up to 12 times. In this
case, yptr will
be pointing to some memory location containing garbage (i.e. there may
be some value but is useless for us). To display the contents where the
yptr is pointing we
can use cout with
dereference pointer as:
cout << *yptr ;
Page 163
The above statement will display the contents where
yptr is pointing. If the
yptr is
pointing to the first element of the array,
cout << *yptr will display
the contents of the
first element of the array (i.e. y[0]). While incrementing the
yptr as
yptr ++, the
statement cout << * yptr
will display the contents of the 2nd element of the array(i.e.
y[1]) and so on.
Here is an example describing different methods to access array elements.
/* This program contains different ways to access array elements */
#include <iostream.h>
main ()
{
int y[10] = {0,5,10,15,20,25,30,35,40,45};
int *yptr;
yptr = y; // Assigning the address of first element of array.
cout << “Accessing 6th element of array as y[5] = ” << y[5] << endl;
cout << “Accessing 6th element of array as *(yptr + 5) = ” << *(yptr
+ 5) << endl;
cout << “Accessing 6th element of array as yptr[5] = “ << yptr[5]
<< endl;
}
The output of the program is:
Accessing 6th element of array as y[5] = 25
Accessing 6th element of array as *(yptr + 5) = 25
Accessing 6th element of array as yptr[5] = 25
In the above example, there are two new expressions i.e.
*(yptr+5) and
yptr[5]. In the
statement *(yptr+5),
yptr is incremented
first by 5 (parenthesis are must here).
Resultantly, it points to the 6th element of the array. The dereference
pointer gives the
value at that address. As yptr
is a pointer to an integer, so it can be used as array name.
So the expression yptr[5]
gives us the 6th element of the array.
The following example can explain how we can step through an entire array using
pointer.
/* This program steps through an array using pointer */
#include <iostream.h>
Page 164
main ()
{
int y[10] = {10,20,30,40,50,60,70,80,90,100};
int *yptr, i;
yptr = y; // Assigning the address of first element of array.
for (i = 0; i < 10 ; i ++)
{
cout << “\n The value of the element at position ” << i << “ is “ << *yptr;
yptr ++ ;
}
}
The output of the program is:
The value of the element at position 0 is 10
The value of the element at position 1 is 20
The value of the element at position 2 is 30
The value of the element at position 3 is 40
The value of the element at position 4 is 50
The value of the element at position 5 is 60
The value of the element at position 6 is 70
The value of the element at position 7 is 80
The value of the element at position 8 is 90
The value of the element at position 9 is 100
Consider another example to elaborate the pointer arithmetic.
/* Program using pointer arithmetic */
#include <iostream.h>
main()
{
int x =10;
int *yptr;
yptr = &x;
cout << “The address yptr points to = ” << yptr << endl ;
cout << “The contents yptr points to = ” << *yptr << endl;
Page 165
(*yptr) ++;
cout << “After increment, the contents are ” << *yptr << endl;
cout << “The value of x is = ” << x << endl;
}
The output of the program is:
The address yptr points to = 0x22ff7c
The contents yptr points to = 10
After increment, the contents are 11
The value of x is = 11
Here the statement (*yptr)
++ is read as “increment whatever
yptr points to”. This will
increment the value of the variable. As
yptr and
x both are pointing to
the same location,
the contents at that location becomes 11. Consider the statement
*yptr + 3 ; This is an
expression and there is no assignment so the value of x will not be changed where
as the
statement *yptr += 3;
will increment the value of
x by 3. If we want to increment
the
pointer and not the contents where it points to, we can do this as
yptr ++; Now where
yptr is pointing? The
yptr will be now pointing
four bytes away from the memory
location of x. The
memory location of x
is a part of program, yet after incrementing
yptr,
it is pointing to some memory area, which is not part of the program. Take this
as an
exercise. Print the value of
yptr and *yptr
and see what is displayed? Be sure, it is not
illegal and the compiler does not complain. The error will be displayed if we try
to write
some value at that memory address.
“When a pointer is used to hold the memory address of a simple variable, do not
increment or decrement the pointer. When a pointer is used to hold the address of
an array, it makes sense to increment or decrement the pointer “
Be careful while using pointers, as no warning will be given, in case of any problem.
As
pointers can point at any memory location, so one can easily get the computers crashed
by using pointers.
Remember that incrementing the pointer and incrementing the value where the pointer
points to are two different things. When we want to increment the pointer, to make
it
point to next element in the memory, we write as (yptr++);
Use parenthesis when
incrementing the address. If we want to increment the value where the pointer points
to, it
can be written as (*yptr) ++;
Keep in mind the precedence of operator. Write a program
to test this.
The decrement of the pointer is also the same.
yptr --;
yptr -= 3 ; will decrement
the yptr.
Whereas the statement (*yptr)
--; will decrement the value where the
yptr is pointing. So
if the yptr is pointing
to x the value of
x will be decremented
by 1.
Page 166
Pointers are associated to some data type as pointer to integer, pointer to float
and pointer
to char etc. When a pointer is incremented or decremented, it changes the address
by the
number of bytes occupied by the data type that the pointer points to. For example,
if we
have a pointer to an integer, by incrementing the pointer the address will be incremented
by four bytes, provided the integer occupies four bytes on that machine. If it is
a pointer
to float and float occupies eight bytes, then by incrementing this pointer, its
address will
be incremented by eight bytes. Similarly, in case of a pointer to a char, which
normally
takes one byte, incrementing a pointer to char will change the address by one. If
we move
to some other architecture like Macintosh, write a simple program to check how many
bytes integer, float or char is taking with the use of simple pointer arithmetic.
In the
modern operating systems like windows XP, windows 2000, calculator is provided under
tools menu. Under the view option, select scientific view. Here we can do hexadecimal
calculations. So we can key in the addresses our programs are displaying on the
screen
and by subtracting, we can see the difference between the two addresses. Try to
write
different programs and experiment with these.
We have seen that we can do different arithmetic operations with pointers. Let's
see can
two pointers be added? Suppose we have two pointers
yptr1 and
yptr2 to integer and
written as yptr1 + yptr2 ;
The compiler will show an error in this statement. Think
logically what we can obtain by adding the two memory addresses. Therefore, normally
compiler will not allow this operation. Can we subtract the pointers? Yes, we can.
Suppose we have two pointers pointing to the same memory address. When we subtract
these, the answer will be zero. Similarly, if a pointer is pointing to the first
element of an
integer array while another pointer pointing to the second element of the array.
We can
subtract the first pointer from second one. Here the answer will be one, i.e. how
many
array elements are these two pointers apart.
Consider the following sample program:
/* Program using the pointer subtraction */
#include <iostream.h>
main ()
{
int y[10], *yptr1, *yptr2;
yptr1 = &y[0];
yptr2 = &y[3];
cout << “ The difference = “ << yptr2 - yptr1;
}
The output of the program is:
Page 167
The difference = 3
In the above program, we have taken two integer pointers
yptr1 and
yptr2 and an integer
array y[10]. The
pointer yptr1 is
pointing to the address of the first element of the array
while yptr2 is pointing
to the 4th element of the array. The difference between these two
pointers can be shown by using
cout statement. Here the
result should be twelve. But the
program will show the result as three. When we increment an integer pointer by 1,
we
have seen that the address is changed by four. When we subtract pointers, it tells
us the
distance between the two elements that the pointers pointed to. It will tell us
how many
array elements are between these two pointers. As the
yptr1 is pointing to
y[0] and the
yptr2 is pointing to
y[3], so the answer is
three. In a way, it tells how many units of data
type (pointers data type) are between the two pointers. Pointer addition is not
allowed,
however, pointer subtraction is allowed as it gives the distance between the two
pointers
in units, which are the same as the data type of the pointer.
A memory image of an array with a pointer.
This diagram shows how an array occupies space in the memory. Suppose, we have an
integer array named y
and yptr
is a pointer to an integer and is assigned the address of the
first element of the array. As this is an integer array, so the difference between
each
element of the array is of four bytes. When the
yptr is incremented, it
starts pointing to
the next element in the array.
Pointer Comparison
We have seen pointers in different expressions and arithmetic operations. Can we
compare pointers? Yes, two pointers can be compared. Pointers can be used in
conditional statements as usual variables. All the comparison operators can be used
with
pointers i.e. less than, greater than, equal to, etc. Suppose in sorting an array
we are using
yptr
y[0] y[1] y[2] y[3] y[4]
Addresses:
3000 3004 3008 3012 3016
yptr++ yptr
Page 168
two pointers. To test which pointer is at higher address, we can compare them and
take
decision depending on the result.
Again consider the two pointers to integer i.e.
yptr1 and
yptr2. Can we compare
*yptr1
and *yptr2? Obviously
*yptr1 and
*yptr2 are simple
values. It is the value of integer
yptr1, yptr2
points to. When we say *yptr1 > *yptr2, this is a comparison of simple two
integer values. Whenever we are using the dereference pointer (pointers with *),
all
normal arithmetic and manipulation is valid. Whenever we are using pointers themselves,
then certain type of operations are allowed and restrictions on other. Make a list
what can
we do with a pointer and what we cannot.
Consider a sample program as follows:
/* Program using the dereference pointer comparison */
#include <iostream.h>
main ()
{
int x, y, *xptr, *yptr;
cout << “ \n Please enter the value of x = “ ;
cin >> x ;
cout << “ \n Please enter the value of y = “;
cin >> y ;
xptr = &x;
yptr = &y;
if (*xptr > *yptr )
{
cout << “ \n x is greater than y “;
}
else
{
cout << “\n y is greater than x “;
}
}
The output of the program is;
Please enter the value of x = 6
Please enter the value of y = 9
Page 169
y is greater than x
Pointer, String and Arrays
We have four basic data types i.e. char, int, float and double. Character strings
are arrays
of characters. Suppose, there is a word or name like Amir to store in one entity.
We
cannot store it into a char variable because it can store only one character. For
this
purpose, a character array is used. We can write it as:
char name [20];
We have declared an array name
of 20 characters .It can be initialized as:
name[0] = ‘A’ ;
name[1] = ‘m’ ;
name[2] = ‘i’ ;
name[3] = ‘r’ ;
Each array element is initialized with a single character enclosed in single quote.
We
cannot use more than one character in single quotes, as it is a syntax error. Is
the
initialization of the array complete? No, the character strings are always terminated
by
null character ‘\0’. Therefore, we have to put the null character in the end of
the array.
name[4] = ‘\0’ ;
Here we are using two characters in single quotes. But it is a special case. Whenever
back
slash ( \ ) is used, the compiler considers both the characters as single (also
known as
escape characters). So ‘\n’ is new line character, ‘\t’ a tab character and ‘\0’
a null
character. All of these are considered as single characters. What is the benefit
of having
this null character at the end of the string? Write a program, do not use the null
character
in the string and try to print the character array using
cout and see what happens?
cout
uses the null character as the string terminating point. So if
cout does not find the
null
character it will keep on printing. Remember, if we want to store fifteen characters
in an
array, the array size should be at least sixteen i.e. fifteen for the data and one
for the null
character. Do we always need to write the null character at the end of the char
array by
ourselves? Not always, there is a short hand provided in C, i.e. while declaring
we can
initialize the arrays as:
char name[20] = “Amir”;
When we use double quotes to initialize the character array, the compiler appends
null
character at the end of the string.
Page 170
“Arrays must be at least one character space larger than the number of printable
characters which are to be stored”
Example:
Write a program which copies a character array into given array.
Solution:
Here is the complete code of the program:
/* This program copies a character array into a given array */
#include <iostream.h>
main( )
{
char strA[80] = "A test string";
char strB[80];
char *ptrA; /* a pointer to type character */
char *ptrB; /* another pointer to type character */
ptrA = strA; /* point ptrA at string A */
ptrB = strB; /* point ptrB at string B */
while(*ptrA != '\0')
{
*ptrB++ = *ptrA++; // copying character by character
}
*ptrB = '\0';
cout << “String in strA = ” << strA << endl; /* show strA on screen */
cout << “String in strB = ” << strB << endl; /* show strB on screen */
}
The output of the program is:
String in strA = A test string
String in strB = A test string
Page 171
Explanation:
Suppose, we have declared a char array named
strA of size 80 and initialized
it with some
value say “A test String” using the double quotes. Here we don’t need to put a null
character. The compiler will automatically insert it. But while declaring another
array
strB of the same size, we declare two char pointers *ptrA
and *ptrB.
The objective of this
exercise is to copy one array into another array. We have assigned the starting
address of
array strA to
ptrA and
strB to
ptrB. Now we have to run
a loop to copy all the characters
from one array to other. To terminate the loop, we have to know about the actual
number
of characters or have to use the string termination character. As we know, null
character
is used to terminate a string, so we are using the condition in 'while loop' as:
*ptrA != ‘\0’
, simply checking that whatever
ptrA is pointing to is
not equal to ‘\0’. Look at the
statement *ptrB++ = *ptrA++.
What has happened in this statement? First of all,
whatever ptrA is
pointing to will be assigned to the location where
ptrB is pointing to.
When the loop starts, these pointers are pointing to the start of the array. So
the first
character of strA
will be copied to the first character of
strB. Afterwards, the pointers
will
be incremented, not the values they are pointing to. Therefore,
ptrA is pointing to the
2nd
element of the array strA
and ptrB
is pointing to the 2nd element of the array
strB. In the
2nd repetition, the loop condition will be tested. If
ptrA is not pointing to
a null character
the assignment for the 2nd element of the array takes place and so
on till the null character
is reached. So all the characters of array
strA are copied to array
strB. Is this program
complete? No, the array strB
is not containing the null character at the end of the string.
Therefore, we have explicitly assigned the null character to strB. Do we need to
increment the array pointer? No, simply due to the fact that in the assignment statement
(
*ptrA++ = *ptrB++;), the pointers are incremented after the assignment. This program
now successfully copies one string to other using only pointers. We can also write
a
function for the string copy. The prototype of the function will be as:
void myStringCopy (char *destination, const char *source) ;
This function takes two arguments. The first one is a pointer to a char while second
argument is a const pointer to char. The destination array will be changed and all
the
characters from source array are copied to destination. At the same time, we do
not want
that the contents of source should be changed. So we used the keyword const with
it. The
keyword const makes it read only and it can not be changed accidentally. If we try
to
change the contents of source array, the compiler will give an error. The body is
same, as
we have seen in the above program.
This function will not return anything as we are using pointers. It is automatically
call by
reference. Whenever arrays are passed to functions, a reference of the original
array is
passed. Therefore, any change in the array elements in the function will change
the actual
array. The values will be written to the original array. If these are simple variables,
we
will have to send the address and get the called program to change it. Therefore,
we do
not need to return anything from this function after successfully copying an array
into the
other.
Here is the code of the function. Write a program to test this function.
Page 172
void myStringCopy (char *destination, const char *source)
{
while(*source != ‘\0’)
{
*destination++ = *source++;
}
*destination = ‘\0’;
}
We can also write the string copy function using arrays. Here is the code of the
myStringCopy function using arrays notation.
void myStringCopy(char dest[], char source[])
{
int i = 0;
while (source[i] != '\0')
{
dest[i] = source[i];
i++;
}
dest[i] = '\0';
}
Exercise:
1) Print out the address and the value of a character pointer pointing to some
character.
2) Write a function which copies an array of integers from one array to other
Tips
• While incrementing the pointers, use the parenthesis
• Increment and decrement the pointers while using arrays
• When a pointer is incremented or decremented, it changes the address by
the
number of bytes occupied by the data type that the pointer points to
• Use key word const with pointers to avoid unwanted changes
• The name of array is a constant pointer. It cannot be reassigned
|
|
|
|