SlideShare a Scribd company logo
By: Asaye Chemeda Email: asayechemeda@yahoo.com 26
CHAPTER THREE
POINTERS
Introduction
The data types in C++ are broadly categorized into simple
data types, structured data types and pointers. We have
already seen the first two. In this chapter, we will deal
about the third categories of data types in C++, i.e.,
pointers. Their name is coined from the fact that these
data types point to another data. Pointer variables, or
shortly pointers, are data types which are derived based
on other data types. There is also no name associated with
the data type of pointers. However, they are considered
to be a separate data types in C++.
Pointer variables are used to store memory addresses of
the variables they point to. The variables which are
pointed to by pointers have a name for their data types,
unless they are pointers themselves. Even if the data type
of pointers has no name in C++, pointers are usually
considered to have the same data type as the variable they
point to and are defined as such. However, there are also
pointers called generic pointers with void data type.
These types of pointers can point to any variable with any
data type. They can also be assigned with other pointers
which point to any data type.
Since pointers refer to the memory addresses of other
variables, they are very powerful means for manipulation
of data in C++. Pointers will provide you the way by
which you can manipulate data which other conventional
approaches fail to do. Thus, they are very useful tools in
C++. However, they may also have undesirable
consequences unless they are handled carefully in a
program.
Defining Pointers
Pointers take the same data type but different content
than the variables which they point to. If so, how are these
pointer variables are defined? What makes them be
distinguished from non-pointer variables?
Pointer variables are distinguished from other non-
pointer variables by using the asterisk symbol, ‘*’, during
their definition. If the asterisk symbol is put in front of a
variable during its declaration, the compiler will consider
it to be a pointer. The asterisk symbol can appear
anywhere between the data type and the pointer variable.
However, it is a good practice to pre-attach it with the
pointer variable. The syntax used to define a pointer
variable is:
In the above declaration, the dataType , which can also
be void, is usually the same as the data type of the
variable that the pointerVariable points to.
Once a pointer variable is defined, how it is then used for
data manipulation? Before answering this question, we
will discuss about two operators which are often used
with pointers. They are the address of operator denoted
by the ampersand symbol, ‘&’ and the dereferencing or
at address or indirection operator denoted by the
asterisk symbol,’*’. The address of operator returns the
memory address of its operand while the dereferencing
operator returns the value stored at a given memory
location. To understand this a bit more, let us see the
following piece of code.
In the above code, an integer variable x is defined and
initialized with a value of 100 on line 1. As you know,
when values are declared, a memory space is allocated for
the variables during compile time. Those memory
locations have addresses. Let us say that the memory
address where x is stored, amongst others, is 1000. Let us
depict this diagrammatically as follows.
The above diagram shows that at memory location 1000,
an integer value of 100 is stored.
On line 2, a pointer variable p is declared by using the *
symbol. Which means that the variable p will store
memory address of another variable, i.e., p is declared to
point to another variable. The int data type that the
pointer variable is declared by indicates that the pointer
will point to a variable with int data type. Again, a separate
memory location will be allocated for this variable. Let us
say the memory location allocated for p has an address of
1003. When we depict this graphically, as in the above
diagram, we will have the following.
The above diagram shows that a memory address of 1003
is allocated for the additional variable p. Note that, the
pointer variable is not yet initialized and at this stage it can
point to any memory location. If your program tries to
By: Asaye Chemeda Email: asayechemeda@yahoo.com 27
access memory address 1003 which is not yet initialized
and use the address which it points to now, your program
may be directed to any memory location even those
having sensitive system data and your computer system
may crash. Line 3 shows how a pointer variable is
initialized.
On line 3, p is assigned with the address of variable x by
using the address of operator &. The part on the right
hand side of the assignment operator ( = ), ‘&x’, returns
the memory address of variable x. The assignment
operator will assign this address to the pointer variable p
on the left hand side. Which means that the memory
address of variable x, which is 1000, will be stored in the
memory address of p, which is 1003. In short, p is now
pointing to variable x. After this, the memory locations
will appear to be:
In the above diagram, the memory address 1003 now
contains another memory address 1000, which is the
memory address of variable x. After the initialization on
line 3, the variable p points to a specific known address
and you can use it without any harm to your computer.
On line 4, the value at address contained by variable p is
assigned with value of 25 by using the dereferencing
operator *. The part on the left hand size of assignment
operator of the statement, *p, refers to the value stored
at memory address contained by pointer variable p. The
memory address contained by p is 1000 and the value at
address 1000 was 100. Therefore, the statement on line 4
changes this value to 25. Diagrammatically, what happens
after line 4 is shown as follows:
In the above diagram, the value at address 1000 is now
changed to 25. Note that, memory address 1003 still
contains the memory location of variable x and further
changes can be made through the same procedure.
However, the pointer variable p can also be made to point
to another variable.
The way how pointers manipulate data in C++ is as
simple as what we discussed above. But that is not all
about pointers. Based on the above concept, we can
utilize pointers to access and manipulate data in different
ways. Before proceeding to those details, don’t you think
it is good if we can incorporate the above piece of code
into a full program and see the results? If that is also your
thought, let us see the following program.
Program 3.1[Correct].cpp
When the above program is run, the resulting output is
shown below. Note that, the address results are machine
dependent and you may obtain different addresses than
those shown below.
What we confirmed from the above output is that (1) The
address of x can be obtained by either the address of
operator & or by the pointer variable p which points to
the variable x (Line 9 of the program and line 1 of the output).
(2) A memory location is also allocated for the pointer
variable p which can be obtained by the & operator (Line
11 of the program and line 2 of the output). (3) The value of the
variable x can be obtained from the variable itself or from
its pointer variable p by using the dereferencing operator
*. (Line 13 of the program and line 3 of the output). (4) The
value of the variable x can be changed by using the *
operator and its pointer variable p. (Line 15 of the program
and line 4 of the output).
Have you noticed that pointers are variables themselves?
Their difference from other non-pointer variables are due
to the type of data they store being different from other
simple or structured data types. You may ask the
following string of questions. If pointers are variables, can
we perform arithmetic operations on them? If it is
possible, what do the results of arithmetic operations on
pointers refer to? Do the results even have any real
purpose in programming? All these questions will be
answered in the following topic.
Pointer Arithmetic
Besides being assigned with and compared to another
pointer variable, only the following arithmetic operations
can be performed on pointers.
1. Pointer variables can be incremented or
decremented.
By: Asaye Chemeda Email: asayechemeda@yahoo.com 28
2. Integer values can be added or subtracted from
pointers.
3. One pointer variable can be deducted from
another pointer variable.
Note that any other arithmetic operation is prohibited on
pointers. Understanding the above arithmetic operations
to a modest detail is helpful and they will be discussed
below.
Assigning pointer with another pointer or a value. A
pointer is not only assigned or initialized with the help of
the & operator. A pointer can also be assigned or
initialized with another pointer. After the assignment,
both the assigned and the assigning pointers refer to the
same memory location. Let us have a look at the following
piece of code.
On line 1, a character variable c is declared and initialized
with a character literal ‘A’. On line 2, pointers p1 and p2
are declared. On line 3, p1 is initialized with the address
of c. On line 4, p2 is assigned with p1. After line 4, both
p1 and p2 will point to c. What each line of the above
piece of code will do is graphically depicted as below.
Note that the memory addresses are arbitrary.
Pointers of any data type can also be assigned with only
one value, i.e. ‘0’. Pointers which are assigned with this
value are called null pointers and they point to nothing.
Null pointers can also be created by assigning pointers of
any data type with the word ‘NULL’. Null pointers are
conventionally used to avoid problems caused by
uninitialized pointers.
Comparison of pointer variables. A comparison can be
made between two pointers. In general, pointers can be
used in logical comparison statements. These
comparisons, however, are based on the equality or
otherwise of the addresses contained by pointers. For
instance, if p and q are two pointers,
• p == q returns true if the memory address in p
is equal to that in q or false otherwise.
• p < q returns true if the memory address in p is
less than that in q or false otherwise.
• p > q returns true if the memory address in p is
greater than that in q or false otherwise.
Such pointer comparisons might be useful if we are
interested to know whether two pointers refer to the same
memory address or not. However, the purpose of pointer
comparison is not limited to this and you can use the
concept whenever appropriate.
Incrementing or decrementing of pointers. The values
of pointers can be incremented by the ++ operator or
decremented by the -- operator. What the incrementing
or decrementing operations will do the pointer variable is
interesting. When a pointer is incremented or
decremented, the value by which the pointer will do as
such depends on the data type of the pointer. For
character pointers which point to characters which always
have 1byte size, an increment adds one to the pointer
where as a decrement subtracts one from the pointer. For
integer pointers which point to int variables which
normally have 4byte size, an increment adds four to the
pointer where as a decrement subtracts four from the
pointer. In short, the increment or decrement of pointers
is done by an amount equal to the memory size allocated
for a single base data type.
Usually, memory addresses correspond to hexadecimal
numbers. However, for simplicity purposes, we will use
memory addresses with integer values and we will see how
incrementing and decrementing is done for pointers
which point to different data types. Since the procedure is
similar for different data types, we will only deal with char
and int data types.
Increment and decrement in char pointers
Let us refer to the following piece of code.
On line 1 of the above of code, character variables c1,
c2 and c3 are declared and initialized with character
literals ‘A’, ‘B’ and ‘C’ respectively. On line 2, a pointer
pChar with char data type is declared and initialized with
address of c1. At this point, pChar points to c1. Let us
assume that c1, c2 and c3 are stored in contiguous
By: Asaye Chemeda Email: asayechemeda@yahoo.com 29
memory locations with addresses of 2000, 2001 and 2002
respectively and pChar is stored at memory address
2004. At this point, pChar and *pChar are equal to
2000 and ‘A’ respectively. This is depicted in a diagram
as follows.
When pChar is incremented on line 3, the memory
address in pChar will be incremented by the amount of
the memory size of a single character variable, which is 1.
After the increment, the memory address in pChar
becomes 2001. Which means that pChar now points to
c2. Thus, pChar and *pChar are equal to 2001
and ‘B’ respectively. Diagrammatically, this is represented
as:
When pChar is incremented on line 4 one more time,
the memory address in pChar will be incremented by 1.
After the increment, the memory address in pChar
becomes 2002. Which means that pChar now points to
c3. Thus, pChar and *pChar are equal to 2002
and ‘C’ respectively. When this is put in diagram, we will
have:
When pChar is decremented on line 5, the memory
address in pChar will be decremented by 1 and becomes
2001. Which means that pChar now points to c2
again. Thus, pChar and *pChar are equal to 2001
and ‘B’ respectively. When this is put in diagram, we will
have:
Increment and decrement in int pointers
Let us refer to the following piece of code.
On line 1 of the above of code, int variables i1, i2 and
i3 are declared and initialized with integer values ‘75’,
‘83’ and ‘49’ respectively. On line 2, a pointer pInt with
int data type is declared and initialized with address of i1.
At this point, pInt points to i1. Since the memory size
of int data types is normally 4bytes, each integer variable
requires four memory cells. Let us assume that i1, i2
and i3 are stored in contiguous memory locations with
the first cell addresses of 10, 14 and 18 respectively and
pInt is stored at memory address 26. At this point,
pInt and *pInt are equal to 10 and 75 respectively.
This is depicted in a diagram as follows. The memory
addresses are written for alternate cells to avoid
congestion of characters.
When pInt is incremented on line 3, the memory
address in pInt will be incremented by the amount of
the memory size of a single int variable, which is 4. After
the increment, the memory address in pInt becomes 14
not 11. Which means that pInt now points to the next
int variable which is i2. Thus, pInt and *pInt
are equal to 14 and 83 respectively. Diagrammatically,
this is represented as:
When pInt is incremented on line 4 one more time, the
memory address in pInt will be incremented by 4. After
the increment, the memory address in pInt becomes 18.
Which means that pInt now points to i3. Thus,
pInt and *pInt are equal to 18 and 49
respectively. When this is put in diagram, we will have:
When pInt is decremented on line 5, the memory
address in pInt will be decremented by 4 and becomes
14. Which means that pInt now points to i2 again.
Thus, pInt and *pInt are equal to 14 and 83
respectively. When this is put in diagram, we will have:
Isn’t the way that the compiler handles incrementing and
decrementing operations on pointers fascinating? Indeed
By: Asaye Chemeda Email: asayechemeda@yahoo.com 30
it is. But, have you noted this? In the above examples we
assumed that the variables are stored in contiguous
(interconnected) memory locations. But, is that always the
case? Not necessarily. Simple data type values may not be
stored as such. As a result, it may be difficult to keep track
of the memory locations for variables belonging to simple
data types and trying to use the incrementing and
decrementing operators to access values of such data
types may result in wrong calculation results or data
corruption. If so, what is the importance of incrementing
and decrementing pointers?
Here is the answer for the above question. Some data
types are stored in the main memory in a structured
manner. Structured data types such as arrays are good
example for this. When elements of arrays are stored in
the main memory, the compiler will allocate contiguous
memory locations for them. Therefore, if the address of
the first element is known, the address of the other
elements can easily be tracked based on the memory size
requirement for each element.
As you may recall, the identifier used for defining arrays
refers to the address of the first element. In other words,
the identifier of an array is a pointer to the first array element. The
identifier can be assigned to a pointer. Based on a pointer
which is assigned with identifier of an array, the memory
addresses of all the other elements can easily be obtained
through incrementing and decrementing. You see, that is
where the incrementing and decrementing operations on
pointers become very useful.
In chapter two, we have seen how arrays are passed to
functions. Do you remember how it was done? It was by
reference, right? After our recent discussion, do you see
another way by which arrays can be passed to functions?
Let me give you a hint. Just remember how the following
four things are done. How value is transferred from the
argument passed to the formal parameter list of the
function when a function is called. How pointers are
declared. What the identifier of an array refers to. And
how pointer increment and decrement is done.
When we revise how the above four things are done, we
will get the following. When a function is called by passing
arguments, the called function defines and assigns the
variables in its formal parameter list with the passed
arguments according to their order. Pointer variables are
declared by pre-attaching * to the name of the pointer
variable. The identifier of an array is a pointer to the first
element of the array. Pointer incrementing and
decrementing is normally done to access and manipulate
data in contiguous memory locations by increment and
decrement operators.
Now, let us see how we can take the above knowledge to
our advantage to pass arrays to functions and manipulate
the elements of the array. When a function call is made to
pass an array, let the calling function pass the identifier of
the array. Let us make the corresponding argument on the
formal parameter list of the called function to be a
pointer. Which means that, during function call, the array
identifier, which is a pointer, will be assigned to the
corresponding pointer variable in the called function.
Inside the body of the called function, all the array
elements can now be accessed and manipulated by
incrementing and decrementing the pointer which is
assigned by the array identifier. Don’t you think it is a
good strategy? Of course it is. This way of passing arrays
to functions is termed as passing arrays by pointers and
if the program is well-written, it is the neatest way to pass
arrays. Note that, the changes made on the pointers of
array elements inside the body of the function is also
reflected in the calling environment. The above way of
passing arrays is applicable for one-dimensional arrays.
Passing two-dimensional arrays can also be done through
pointers. But, let us keep discussing this concept aside
until we start dealing with the ‘pointers and arrays’ topic.
I feel that we have discussed lots of concepts and we need
to take a break. Do you also feel the same? However, in
the meantime, let us write a program through which we
will be able to understand what we discussed so far about
arithmetic operations on pointers as well as about passing
arrays by pointers. Here is the program.
Program 3.2 [Correct].cpp
By: Asaye Chemeda Email: asayechemeda@yahoo.com 31
The above program calculates the cube roots of the first
five positive perfect cubes by the cubeRoot function
and prints the cube roots by the print function. We will
only discuss the important points in the program.
On line 11, the cubeRoot function is called and the
identifier of the cube array is passed as an argument.
Technically, what is passed as an argument is a pointer to
the first element of the cube array. When the
cubeRoot function is invoked by this call, the first thing
the function is going to do is assigning its formal
parameter accordingly with the passed argument. In C++
statement, this assignment is represented as:
Isn’t this how a pointer is declared initialized with another
pointer? Of course it is. In the above declaration, we need
to understand that the data type of c is double and it can
only be initialized with a pointer of only double data type.
After the above declaration the pointer c will contain the
memory address of the first element of the cube array.
On line 17, the cube root of the value at address contained
by c is calculated and stored in the same address
contained by c. On line 18, the pointer is incremented.
After each increment, the pointer c will point to the next
element in the contiguous memory location, i.e., the next
element of cube array. The for-loop controls for how
many times that the increment will be done. When the
loop is complete, all the elements of the cube array will
now contain the cube roots of the corresponding original
values. This change is also reflected in the main function
where the cubeRoot function is called. Therefore,
when the array identifier cube is now passed to the print
function on line 12, it refers to the memory address of the
first element of the changed cube array elements not those
elements given on line 10.
Note that the data type of the formal parameter of the
print function is void. This means that, the cubeRt
pointer is a void pointer which can be assigned with
another pointer which points to a variable of any data
type. However, to access and manipulate the data
contained by the cubeRt pointer, explicit type casting
should be made. Type casting is conversion of one data
type into another. One way to do this in C++ is by putting
the data type to be converted to within brackets in front
of the variable which we want to convert its data type as
it was done on line 22. However, type casting may not be
applicable for conversion between some data types. Note
that the casting on line 22 could also be done by using the
static_cast key word and the data type to be casted
to within angled brackets as in the following statement:
On line 22 of the program, the void cubeRt pointer is
casted or converted to double pointer and the casted
pointer is assigned to a double pointer named x. Through
pointer x, the elements of the array x can now be
accessed and printed by using the increment operator.
The part of the statement on line 24, *x++, means access
the value stored at address contained by x and increment
the pointer x to the next value.
I hope the above example clears any doubts you might
have regarding pointer incrementing and passing one-
dimensional arrays to functions as pointers. Remember,
we haven’t yet covered all the possible arithmetic
operations on pointers; two more are remaining.
Adding or subtracting integers from pointer
variables. If you understand how incrementing and
decrementing of pointers is done, understanding how
adding or subtracting of integers is done is pretty easy. As
you know, when incrementing is done by the ++ operator,
the value of the variable on which the incrementing is
done will be increased by sizeof(base data type of the
variable). The keyword sizeof is a reserved C++ word
used to return the size of the memory space allocated for
objects. The value sizeof(base data type) of may not
merely mean one. It may mean one for char pointers,
four for int pointers or eight for double pointers.
Adding an integer i to a pointer, therefore, means adding
i times size of the pointer variable. The same is true for
subtracting integers. Let us see this through the following
piece of code.
In the above piece of code, a double variable x is defined
and initialized on line 1. On line 2, a double pointer p is
defined and initialized with the address of x. Say the
address of x is 100. After line 2, the value of p will be 100.
Double data types normally require 8bytes. A single
increment on pointer p by ++ will result in a value of p
to be 108. However, on line 3, the value of p is increased
by 10. What do you think will the value of p become after
line 3 when the value of p after line 2 is 100? Are you
saying 110? If you say so, you might have forgotten how
incrementing of pointers is done.
When 10 is added to a double data type pointer variable,
the value of the pointer will be incremented by 10 times
sizeof(double) (i.e., 8). In other words, when 10 is
added to pointer p, the value of the pointer will be
incremented by 80. Therefore after line 3, the value of p
becomes 180.
Subtracting integers from pointers is also done in a similar
procedure.
By: Asaye Chemeda Email: asayechemeda@yahoo.com 32
Deducting two pointer variables. Two pointers
pointing to the same data type may also be deducted from
each other. The difference between two pointers results
in an integer value. The absolute value of the resulting
integer represents the number of objects of the base data
type between the pointers. The difference between two
pointers, however, cannot be assigned to another pointer
even if the difference is equal to zero.
We have now seen the only possible arithmetic operations
on pointers. The following operations, amongst others,
are prohibited.
• Adding two pointers
• Assigning non-zero values to pointers
• Adding or subtracting floating-point data type
values from pointers.
• Multiplying or dividing two pointers.
At this point, we have discussed the basics of pointers and
pointer operations. Let us proceed further and discuss
other details. Have you ever imagined that there could
also be array of pointers? Indeed, there are arrays of
pointers too.
The concept of arrays of pointers integrates the concepts
of both arrays and pointers. Arrays of pointers are arrays
which contain pointers as elements. They are declared and
used in the same way as other arrays. The only difference
is the asterisk symbol should be used to tell the compiler
that the array is an array of pointers. For instance, an array
of int pointers x having 10 elements can be declared as
follows.
What would x store if it was just an int array declared
without the asterisk? Ten integer values, right? Because of
the asterisk, however, x is declared as a pointer array and
it stores ten memory addresses with each memory address
pointing to an int variable. After this declaration, x can
be initialized with memory addresses of other variables. If
we want to initialize the fourth element of x with the
address of an int variable y, we can write the following:
Now, if we want to initialize the value of y by 76, we can
use the following statement:
We have discussed that pointers point to other variables
which have their own data types. The interesting question
here is: can pointers point to another pointers? Of course,
they can. Simply, ask yourself what the name of array of
pointers represents. If you understand what kind of
pointer that a name of array of pointers is, reading the
next topic might not be necessary. Otherwise, go through
the next topic for a better understanding.
Multiple Indirection
Pointers are variables themselves and a memory location
is allocated for them. If a pointer stores the memory
location allocated for another pointer variable, the pointer
is pointing to another pointer variable. In C++, the
process of pointing at another pointer is known as
multiple indirection. The pointer with multiple
indirection is known as pointer to a pointer. The process
of multiple indirection can be repeated as many as we
want although more than three multiple indirection is
rarely used in programming.
Pointers to pointers can be defined in the same way as
other pointers with only one indirection. The only
difference is, as many asterisk symbols as the number of
indirections will be put between the pointer and data type
of the pointer. The data type of pointers with multiple
indirection will be the same as the data type of the target
value.
A pointer with two indirections is declared with the
following syntax.
In the above declaration, the two asterisk symbols
between the pointerVariable and dataType
indicate that the pointerVariable will point to
another pointer.
Pointers to pointers can only be assigned with address of
other pointers or ‘0’. The way how we use the address of
operator (&) and the indirection operator (*) while using
pointers to pointers is the same as simple pointers. The
target value can also be accessed and manipulated by
pointers to pointer by using as many indirection operators
as they are used to define the pointer to pointer.
It is easy, right? If not, the following simple program will
make it so.
Program 3.3[Correct].cpp
By: Asaye Chemeda Email: asayechemeda@yahoo.com 33
In the above program, a float variable x is defined and
initialized on line 6. A pointer p and a pointer to pointer
pTp are defined on line 7. Note that, if p is supposed to
point to x and if pTp is supposed to point to p, the data
type of pTp will also be the same as the data type of x.
that is the reason why p and pTp were defined as such.
On line 8, the address of variable x is assigned to p and
on line 9, the address of p is assigned to pTp. Note that,
trying to assign pTp with address of a non-pointer
variable such as x rather than with address of a pointer
would have resulted in compilation error.
From the above program, the main expected outcome is
that the output of line 10 and line 13 to be the same. If
you understand why this should be, it means that you have
understood multiple indirection. If not, there should be
no worries as we will go through the output of the five
cout statements.
The output of the statement on line 10 is the address of
the variable x contained in p. Since p is assigned with the
address of p on line 8, displaying the value of p by cout
statement will result in displaying the memory address of
x.
The value of pTp will be displayed by the cout
statement on line 11. The output of this line is the
memory address of p contained in pTp. This is because
pTp is assigned with the address of p on line 9.
Through the indirection operator, the value at address
contained in p will be displayed as an output of line 12.
What is address contained in p? It is the address of
variable x, right? Therefore, *p access the value of x.
When we come to the statement on line 13, a value
referred by *pTp will be printed. This means the value at
address contained by pTp will be displayed. On line 9,
pTp is assigned with the address of p. Therefore, pTp
contains the address of p and *pTp accesses the value at
the address contained by pTp which is the value of p
which is also the address of x. Therefore, *pTp and p
refer to the same thing and the output of line 10 and line
13 will be the same.
The value of x can also be accessed through pTp by
applying the same number of indirection operators as it
was used to define pTp. The cout statement on line 14,
which prints the value of **pTp, actually prints the value
of x. We have seen above that when the * is pre-attached
once on pTp, it accesses p, when another one is pre-
attached, the value of x will be accessed.
Although the memory addresses are machine dependent,
the output of the above program is shown below. As can
be observed, the memory addresses printed on the first
and forth lines of the output are the same which asserts
our explanation above.
I guess you have mixed feelings about multiple
indirections. On one hand, you may wonder how pointers
to pointers work to access and manipulate values. One the
other hand, you may be curious about what the purpose
of defining pointers to pointers is. Am I right? If so, my
suggestion is to keep on your wondering about multiple
indirections. And the following topic will answer your
curiosity.
Pointers and Arrays
In C++, pointers and arrays are interrelated. The second
chapter on arrays and our discussion so far in this chapter
about pointers have provided us the subtle differences
between them. In C++ programming, pointers are often
used to access and manipulate array elements. Arrays can
also be used as pointers. In our discussion about arrays in
chapter two, we have said that array names refer to the
memory locations of the first array elements. Therefore,
array names are pointers themselves. Even if array names
are pointers, you can’t perform pointer arithmetic on
them which attempts to change their values. In general,
array names are constant pointers.
You may ask, in what way are arrays and pointers are
interrelated? We have already seen how one-dimensional
arrays and pointers can be interrelated during our
discussion on incrementing and decrementing pointers.
The concepts which were not discussed then, will be
covered in this topic.
Let us start discussing more about the relationship
between arrays and pointers for one dimensional arrays.
Consider the following one-dimensional array
declaration:
In the above array the name numbers is a pointer to the
first array element, i.e. it contains the memory address of
the value 10. Through indexing, the first element is
accessed by numbers[0]. Therefore, the following
logical statement returns true.
By: Asaye Chemeda Email: asayechemeda@yahoo.com 34
The above logical statement compares the equality
between numbers and the address of numbers[0].
Since the returned value is true, it means that
numbers contains the address of numbers[0]. Thus,
numbers is a pointer. If numbers is dereferenced, it
gives the value of the first array element. The following
logical statement will, therefore, return true.
The first array element can also be changed to another int
value, say 15, by using the array name through the
following statement.
So far so good. The array name, numbers, is behaving
exactly the same as other regular pointers. Can we
conclude that array name can be used as like any other
pointer? Before rushing to the conclusion, let us apply
some arithmetic operations on numbers and let us see
how it behaves.
First, let us apply the incrementing operation on
numbers and let us point to the next element of the
array. Recall that incrementing pointers will make them
point to the next element in contiguous memory
locations. Trying to point to the next element of the
numbers array through the following statement,
however, results in compilation error.
Array names are not like other pointers, after all. Can you
guess what the reason for the compilation error could be?
It is the ++ operator. Since the data type of the array is
int, the above statement attempted to increment the value
of numbers by sizeof(int). But, array names are
constant pointers and their values cannot be changed and
they only point to the first array element. This rule is not
limited for only incrementing, any other arithmetic
operation which attempts to change the value of an array
name is illegal. We can now conclude that array names are
pointers with restrictions.
If incrementing and decrementing is illegal on array
names, we may think that, they are useless to point to
other array elements than the first one. However, that is
not true at all. Array names can also be used to point to
all array elements. The reason why the above statement
failed to point to the next array element is because the ++
operator attempted to change the value of numbers,
which is a constant pointer. Any valid arithmetic
operation which doesn’t attempt to change the value of
numbers can point to other elements of the array as
well. Let us modify the above increment statement by this
one:
Both the statements on Line 1 and Line 2 add
sizeof(int) to the value of numbers. The only
difference between them is, the statement on Line 2
doesn’t attempt to change the value of numbers while the
statement on Line 1 does. Therefore, the statement on
Line 2 is legal and it points to the second element of
numbers array. If we dereference it, the second element
of numbers array will be returned. For instance, the
following statement prints the value of the second
element of the array:
And the following statement changes the value of the
fourth element of the array to 32.
By using the name of the array, we accessed and
manipulated the array elements which we wanted. It was
fascinating, right? The above example shows how one-
dimensional arrays can be used as pointers and the
associated restrictions while doing so. Can we say that
one-dimensional arrays and pointers are the same except
the arithmetic restrictions on array names? Not really.
There are subtle differences between them. We can pick
two, for instance. The first one is the difference in the
memory address of pointers themselves and the memory
address referred by array names. The other is
initialization.
We have seen that normal pointers have their own
memory space allocated for them. However, a separate
memory space is not allocated for array names, although
they are considered as pointers. The memory address
associated with array names is that of the first array
element. In general, the memory returned by the address of
operator on one-dimensional array names is the same the memory
address of the first element.
We have also seen that arrays can be initialized during or
after declaration (except for character arrays) using braces.
However, pointers cannot be initialized with braces even
if they can be used as arrays.
Having the above differences between arrays and
pointers, let us now consider character arrays in
accordance with the above concepts. Do you remember
what we said about character arrays in chapter two? We
said that they are given special consideration in C++. One
of the ways in which they are treated differently is, the
whole character array can be printed by printing the array
name. With the concept in mind, let us now investigate
what made character arrays so special in C++. For this,
we will consider the following character array.
By: Asaye Chemeda Email: asayechemeda@yahoo.com 35
To check that the array name program only refers to
the address of the first element, we can see the truth
values of the following logical statements.
From the above, only the statement on Line 1 returns
true. Hence, the name program only points to the
first elements of the array. The following statements will
print characters ‘C’, ‘+’ and ‘+’ respectively.
Any of the elements of the character array can also be
manipulated by using the dereferencing operator.
So far, we are getting what we expected and everything
seems normal. The character array name program is
behaving just like any other array name. If so, what do you
expect the output of the following statement will be?
If the character array name program behaves like other
array names, what is printed by the above statement
would be the memory address of the first program
array element. But, that is not what will be printed. All the
array elements, and probably additional characters, will
be printed. Even if we expect memory address of the
character ‘C’ to be printed, all the characters in the array
will printed as a string. Mysterious, isn’t it?
The reason for the above mystery to happen will be
solved by analyzing property of the ‘<<’ operator. The
way how the ‘<<’ operator will respond when it is called
into action is implemented in the iostream.h file. In
the iostream.h, there are a number of overloaded
functions to handle the responses when this operator is
called in a program.
Recalling our discussion on functions in chapter one,
overloaded functions have the same name but different
signatures. The overloaded function to handle the ‘<<’
operator is overloaded with several data types including
pointers.
Do you remember rule 1 of overloading which we
discussed? The rule can be summarized as: when an
overloaded function is called, the compiler tries to invoke
the function with the best match formal parameter as the
passed argument. Based on this rule, when pointers are
passed to the function responding to calls made to ‘<<’
operator, there are two alternative overloaded functions
with pointer formal parameters to be invoked. One of
them has a data type for its formal parameter as const
void* while the other one has const char*. When
names of non-character arrays are passed as an argument,
the overloaded function with the const void* as a
formal parameter data type will be invoked. As a result,
the memory address contained in the passed pointer,
which is the array name, will be printed by the ‘<<’
operator.
However, when the name of character arrays is passed,
the overloaded function with const char* as a data
type for the formal parameter will be invoked. This
function doesn’t print the memory address contained in
the name of the passed character array. Rather, all the
characters in the array until the null character is
encountered will be printed. If the character array is not
null-terminated, additional characters may also be printed.
Mystery solved!
After all, character arrays behave in the same way as other
arrays. The separate functions which handle exclusively
character arrays, however, treat them differently and
create the illusion that character arrays are different from
others.
We have seen how one-dimensional arrays can be used as
pointers. Now, let us see the reverse- how pointers can be
used as one-dimensional arrays. Again, we will consider a
fragment of a code. Consider the following string array
and string pointer.
In the above code fragment, the name of the string array,
str, which is actually a pointer to the first element of the
array, “C++” is assigned to a string pointer, p. If p is
dereferenced after Line 3, the return will be the string
“C++”. If you answer what the following statement prints
correctly, it means that you have fresh memory of
arithmetic operations on pointers.
In the above statement, the value of p pointer is pre-
incremented by sizeof(string) and then
dereferenced. The pre-increment will make p point to the
second element of str array before being dereferenced.
The dereferencing returns the value of the second
element of the str array. Therefore, the above statement
prints the string “is”.
Is that how a pointer is used as an array? If so, where is
the array subscripting operator ([]) used? These are good
By: Asaye Chemeda Email: asayechemeda@yahoo.com 36
questions. Indeed, accessing array elements without
pointers is characterized by the usage of indexing through
the [] operator. The way the pointer p was used above
to access the elements of str array doesn’t show that
pointers can be used as arrays.
Do you remember how name of array was used as a
pointer to access array elements? The basic idea at that
time was array name is a pointer and by dereferencing the
array name with integer addition or subtraction, the array
elements can be accessed and manipulated. Now, let us
see how array elements are accessed by indexing using the
array name. Since indexing starts from zero, str[0]
refers to the value of the first element of str array,
str[1] refers to the second and so on.
By initializing the p pointer with array name str, we
have synchronized them to refer to the memory address
of the first element of str array. Now, let us try to use
the pointer p as str the way that str could be used as
p to access the array elements. If this is possible, i.e. if we
can access and manipulate elements of str array by
indexing p pointer without using the dereferencing
operator, we can say that pointers can indeed be used as
arrays.
Of course, it is possible to access and manipulate elements
of one-dimensional arrays by indexing pointers. For
instance, p[0] refers to the value of the first element of
str array. The other elements can also be accessed and
manipulated in a similar manner. The following statement
changes the third element of str array from “fun” to
“awesome”.
Note that, in the above statement, the array subscripting
not the dereferencing operator was used on the pointer p
to change the third str element. Note that using index of 2
with pointers may not necessarily refer to the third array element. It
only means the third element from which the pointer is initialized
with the address of. For instance, if a pointer is initialized
with the address of the second element of the array, using
index of 2 with the pointer refers the fourth element of
the array not the third.
Don’t you feel convinced that pointers can be used as
arrays? Hopefully, you do. Now, think about details.
Consider the following program and guess what will be
printed by the cout statements. Note that p is initialized
with the address of the second element of str array. If
you answered both of them correctly, you have
completely understood what we discussed so far about
pointers and arrays.
Program 3.4[Correct].cpp
In the above program, p is initialized with the address of
the second element of str array. Therefore, the indexing
for p starts from the second element of str array. Based
on this, p[0] refers the string “is” and p[1] refers to
the third element of str array. Thus, the output of the
cout statement on line 9 is the string “awesome”.
The array name, however, always refers to the memory
address of the first element. When 1 is added to an array
name and dereferenced, the second element of the array
is always returned. The output of the cout statement on
line 10 is, therefore, the string “is”.
One-dimensional arrays and pointers can be
interchangeably used to access and manipulate array
elements without much difference between them. Is this
also the case for two-dimensional arrays? Can we apply
what we discussed so far about pointers and arrays for
two-dimensional arrays too? The answer for this is a
mixed yes and no. before looking into how two-
dimensional arrays can be used as pointers or vice-versa,
let us see how elements of two-dimensional arrays are
stored in the memory.
When we consider two-dimensional arrays, what we
normally visualize is simple data type variables arranged
in two dimensions. If the array has (m+1) rows and (n+1)
columns, we may expect it to be arranged as below in the
memory.
Figure 1: Two-dimensional array representation
The question is: is this how two-dimensional arrays are
actually stored in the memory? In two dimensions with
rows and columns? Not really. Two-dimensional arrays
are stored in the memory as one-dimensional arrays. The
above two-dimensional array will be stored in the memory
as:
By: Asaye Chemeda Email: asayechemeda@yahoo.com 37
Figure 2: Equivalent one-dimensional array representation
If two-dimensional arrays are stored in the memory as if
they are one-dimensional arrays, don’t you think the
basics of how they could be used interchangeably with
pointers is already explained? We can say so but it may
not be as straight forward as it was for one-dimensional
arrays. The details are therefore important.
Let us start discussing from what the name of a two-
dimensional array represents. In one-dimensional array,
the name of the array just represents a scalar pointer
containing the memory address of the first element. Is
that so for two-dimensional arrays too? Not really. The
name of a two-dimensional array is a pointer to the first
element of a one-dimensional array of pointers. The
elements in this array of pointers are the memory
addresses of the first elements in each row. When this is
represented in diagram, we will have the following:
Figure 3: Two-dimensional array direction
The name of two dimensional array is, therefore, a pointer
to a pointer. Even if the name of two-dimensional array
is a pointer to a pointer, we can’t assign it to a scalar
pointer to pointer.
Are you getting lost? Or is it getting interesting? May be,
if we use examples, it will be easier to understand and
more exciting. Let us consider the following array
declaration:
In the above statement, marks array is declared to have
three columns and is initialized with six elements. The
array will have two rows but the compiler is not interested
in the number of rows. Between the array name and the
array elements, there is an array of pointers. The array
name marks is a pointer to a pointer and doesn’t directly
point to any of the array elements. If marks is
dereferenced once, what is returned is a memory address
not the first array element. To understand what marks
points to, we can think of marks to be an array name for
one-dimensional array of pointers with the following two
elements.
We can also consider the above two elements to be the
array names for the first and second rows of the two-
dimensional marks array respectively. They are memory
addresses themselves but they can be considered as names
of the two rows of marks array. If they are one-
dimensional array names, they refer to the same memory
address as the elements they point to without having
memory addresses of their own.
If the array name marks can be considered to have the
above two elements, the identifier marks is supposed to
take the memory address of &marks[0][0]. As an
array name itself, &marks[0][0], refers to the
memory address of the first element of the two-
dimensional marks array. In other words, the array name
marks also refers to the memory address of element
marks[0][0]. Despite this, dereferencing marks
only once, will not return the first element of the array.
What a single dereferencing on marks returns is the
address of marks[0][0]. However, if marks is
dereferenced twice, the first element will be returned.
Which means that the array name marks is a pointer to
a pointer. The following logical statement will return
true.
Even if marks is a pointer to a pointer, it can’t be
assigned to a scalar pointer to pointer. The following
statement will cause compilation error.
By: Asaye Chemeda Email: asayechemeda@yahoo.com 38
The reason for the compilation error is that pp is a scalar
pointer to pointer while marks is a pointer to an array of
pointers. The above assignment is just like trying to assign
an array variable to a simple data type variable. If the
reason behind the impossibility of above assignment is
not clear, go through what we recently discussed about
two-dimensional array names once again. Incorporating
the above statements in a program with output statements
and analyzing the results might also help for a better
understanding.
If two-dimensional array names are pointers to an array
of pointers, then how are we going to use them to access
and manipulate array elements as pointers? Good
question at the right time. There are two options for this.
One of them is using the array name partially as an array
and partially as a pointer. The other option is by using the
array name completely as a pointer.
In the first option, the array name will be used to access
the intermediate array of pointers by using the array
subscripting operator. After this, the elements of the
intermediate array of pointers will be used to access and
manipulate the array elements through pointer arithmetic
and dereferencing.
In the second option, the data type of the array name will
be casted from pointer to pointer to simply a pointer.
Then, the casted array name will be used to access all the
array elements through pointer arithmetic and
dereferencing without using the array subscripting
operators.
Both of the above options will be discussed in detail one
by one. Although the second option is easier, starting
from the first option creates a smooth transition.
We will again consider the marks two-dimensional array.
Since the array has two rows, consider the array to be a
set of two one-dimensional arrays each representing a
single row. We have said that we can consider the array
name marks to be an identifier for one-dimensional
array of pointers. Each element in the one-dimensional
array of pointers is a pointer to the first element of each
row. Since the actual two-dimensional array has two rows,
the one-dimensional array of pointers will have two
elements. Now, we can write the marks array as follows:
Note that, each element in the above one-dimensional
array of pointers is an array name for each row of the
actual two-dimensional marks array. If marks is an
identifier for a one-dimensional array of two elements,
how can we write the two elements in terms of marks?
It is easy, right? We can write it using the array
subscripting operator as follows:
Since the two elements in the above marks array are
names for each row, we can also write the following
statements.
You see how the name marks along with the []
operator is used as a name for each row. The names
marks[0] and marks[1] will act as constant
pointers. As long as we do not apply arithmetic operation
which attempts to change their values, marks[0] and
marks[1] can now be used as normal pointers. By
dereferencing them, we can access and manipulate the all
the array elements. For instance, the following program
changes the value of marks[0][1] from 89.3 to
92.9 and prints all the elements of marks array.
Program 3.5[Correct].cpp
If you have understood that marks along with a single
[] operator with the row number inside is just an array
name for each row and if you recall how names of one
dimensional arrays (such as each row of marks array) can
be used as pointers, the above program is straight
forward. The output of the above program is shown
below.
Program 3.5 shows how name of two-dimensional array
can be used to access and manipulated all the elements of
the array by the combination of subscripting and pointer
dereferencing. Do you feel that you understood every
piece of it? Or do you feel that have some doubts?
Whichever your feeling is, you are ok. If you understood
it, proceed to what we are going to discuss next.
Otherwise, go through it once again, write your own
programs, analyze outputs from different angles and you
will definitely say “it isn’t that difficult”.
By: Asaye Chemeda Email: asayechemeda@yahoo.com 39
Now, let us proceed to the second and easier way to
access all elements in two-dimensional arrays by using the
array name as a pointer. In this approach, consider the
two-dimensional array to be a continuous one-
dimensional array as shown in Fig. 2. The equivalent one-
dimensional array is formed by appending each row at the
end of the previous row. Can you imagine what should
also be done during the conversion from two-dimensional
to one-dimensional array? Two basic changes should be
made.
The first change should be the data type of the array
name, not the individual array elements. The name of a
two-dimensional arrays is a pointer to array of pointers
while that of one-dimensional arrays is a pointer.
Therefore, if the conversation is to be made, the data type
of the array name should be casted accordingly. The
casting can be done as follows:
As long as we make the explicit casting as above
consistently, the arrayName can be used as just a name
of one dimensional array, i.e., the array name will behave
as a pointer not a pointer to array of pointers. Consider
the following two dimensional array declaration:
Without casting, the array name sum is a pointer to a
pointer. Remember, even if it is a pointer to a pointer,
sum cannot be assigned to a scalar pointer to pointer.
However, by explicitly casting it, we can make it behave
just like an array name of a one dimensional array. We can
imagine the above array to be as such:
The following statement returns a value of 9, which is the
value first element of sum array.
Note that, in the above statement we used only one
dereferencing operator. Even so, we accessed the value of
the first element. Had we tried this without casting sum
to (int *), we would have only got the address of the
element. The value of the second element, which is 4, can
be returned by the following statement.
Therefore, by casting the names of two-dimensional
arrays, we can use them as constant scalar pointers.
Although the casting is essential, it is not the only change
which should be applied to use two-dimensional array
names as scalar pointers. In addition to casting, another
change should also be made while converting two-
dimensional arrays into an equivalent one-dimensional
array. Can you guess what that change could be? It is the
index of each element.
When indexing through [] operator only is used to
access elements of two-dimensional arrays, the row and
column numbers should be explicitly stated. However,
when the two-dimensional array is converted to an
equivalent one-dimensional array, there will only be a
single index-the row number. As a result, the row and
column indexes which are supposed to access element in
the two-dimensional array should be adjusted so that they
can be used to access the same array element in the one-
dimensional array.
When two-dimensional arrays are condensed to form a
one-dimensional array, the equivalent single index to be
used for accessing the elements is determined from three
values: row and column numbers of the element in the
two-dimensional array and number of columns. That is
why it is always mandatory to explicitly specify the column
number during the definition of two dimensional arrays.
Conversion Rule: If a two-dimensional array has n columns, for
indexing puroses, it can be considered as a one-dimensional array in
which the element on the ith row and jth column in the two dimensional
array becomes kth element in the equivalent one-dimensional array,
where k=i*n+j.
If we apply casting and if we implement the above
conversion rule properly to access the right element, using
two dimensional array names as pointers becomes as easy
as it is for one-dimensional array names. Do you agree
with this or do you have a different view? Before deciding,
let us have a look at the following program.
Program 3.6[Correct].cpp
In the above program, a two-dimensional array sum
having three columns is declared and initialized on line 6.
The single index for the equivalent one-dimensional array
k is defined on line 7. Two for-loops were used to
navigate through all the array elements. The outer for-
loop with i counter is navigates across the row and the
inner for-loop with j counter navigates across the
column. If the array is to be converted to an equivalent
By: Asaye Chemeda Email: asayechemeda@yahoo.com 40
one-dimensional array, the index to be used for this array
(k) is calculated on line 10.
The cout statement on line 11, prints two values. The
first one prints the value of an array element through
indexing only. The second one prints the same value
through pointer only. When an element of the array is
printed using pointer arithmetic, first the array name is
casted. Then, the index in the equivalent one-dimensional
array is added through pointer arithmetic to point to each
array element. By dereferencing, the value of the array
element is returned and printed. By the way, we don’t
need two for-loops when accessing elements of two
dimensional arrays through pointers. One for-loop which
navigates through all the elements can also be used. Since
the number of elements in the array is 9, the following
single for-loop prints all the elements of sum array in the
same way the above program did.
What do you think the problem will be if we use the
following piece of code instead of the above one? In other
words, what could go wrong if we use pointer increment
instead of integer addition as in the following piece of
code?
If you recall, the legal pointer arithmetic operations on
array names, the answer is pretty simple. Array names are
constant pointers and their values cannot be changed.
Integer addition just adds an integer to the array name
without changing the memory address pointed by the
name. Integer addition through ++ operator, however,
attempts to change value of the memory address
contained in the array name, which is illegal. That is why
the above piece of code is wrong. Clear enough?
Aren’t you still convinced that using two-dimensional
array names as pointers is not that difficult? What about
after having the following formula?
If a is the name of a two-dimensional array having n
columns and a base data type of dataType, element
a[i][j] can equivalently be accessed and manipulated
using the array name as pointer using the following
formula:
So simple. Using two-dimensional array names as pointers
has been discussed to the detail we need for this chapter.
But, is that all what we are going to discuss about
pointers? Not really. Pointers are one of the powerful
blessings of C++ and mastering them should be a virtue
of someone who wants to be a decent programmer.
Few more interesting concepts are remaining and we will
proceed to next one. Trust me. Our persistence will pay
off.
Before starting the topic of “Arrays and pointers”, we said
that any curiosity that you might have about pointers to
pointers will get response in this topic. The time has now
come to answer what the applications of pointers to
pointers are in C++ programming. One of the
applications of pointers to pointers comes into picture,
when we use pointers as two-dimensional arrays.
Remember that we haven’t yet discussed how pointers
can be used as two-dimensional arrays. Before proceeding
to it, recalling the basics of two-dimensional array names
might be advantageous.
Two dimensional array names are pointers to an array of
pointers. Because of this, the array name cannot be
assigned to a scalar pointer to a pointer. The array of
pointers stands between the name and array elements. As
a result, the array elements cannot be directly accessed by
only a single dereferencing of the array name.
If we want to use pointers as two-dimensional arrays,
there may be several options. One of them is by creating
a pointer which is a replica of the property of the array
name. There is also another option in which pointers to
pointers can be used to create and used as dynamic arrays.
Both the options will be covered in this chapter.
In order to create a pointer with similar properties as a
name of two-dimensional array, the pointer should be a
pointer to an array of pointers. In addition, the elements
in the array of pointers should point to the first element
in each row of the two-dimensional array. If a pointer to
pointer is declared in such a manner, it can be used as a
two-dimensional array itself and the elements of the array
can be accessed by subscripting.
For a better understanding, let us consider the following
two dimensional array.
The above name array is a two-dimensional array with 11
columns and four rows. Each string literal is a row by
itself. The characters in the above array can be arranged
as follows.
By: Asaye Chemeda Email: asayechemeda@yahoo.com 41
If we want to use a pointer to pointer which can be used
as a two-dimensional array, first we need to create an array
of pointers. The array of pointers will have the same
number of elements as the number of rows in the name
array. Let us declare the array of pointers as follows:
Each element of the pointer array p should now be
initialized with the memory address of the first element in
each row of name array as follows:
The scalar pointer to pointer, which can be used a two-
dimensional array, can now be declared and initialized
with the address of the first element of pointer array p.
this is done as follows:
The pointer pp is a pointer to an array of pointers. Isn’t
pp a replica of the name array? Of course it is. Now, we
can use subscripting on pp to access and manipulate any
element of the name array. It just acts as a two
dimensional array. The following for-loop prints all the
characters as they are written during the declaration of
name array.
The scalar pointer to pointer pp was just used as an array.
You see what the application of pointers to pointers could
be. They can be used as arrays provided that they point to
an array of pointers having elements, which in turn point
to the first elements in each row of a two-dimensional
array.
The above procedure shows one of the ways in which
pointers can be used as two dimensional arrays. Before
proceeding to the last topic of this chapter, let us recall
what we postponed to do in this topic. Can you guess
what that might be? It is about passing two-dimensional
arrays to functions. We put off its discussion, remember?
To pass two dimensional arrays to functions, we can use
different approaches. We can cast the array and pass it as
a scalar pointer. We can define array of pointers with each
element pointing to the element in the first column of the
two-dimensional array. Then, we can pass the array
pointers to a function which receives pointer arrays or
scalar pointers to pointers.
The following program shows three different approaches
to pass two dimensional arrays. In the program, three
functions will be defined and all of them print the last
character of the name array given above.
Program 3.7[Correct].cpp
In the above program, the name array is passed to
print1 function by casting it to a scalar character
pointer on line 16. The formal parameter of print1
function is a scalar pointer and through pointer arithmetic
each element of the name array can be accessed and
manipulated.
An array of pointers p is declared on line 17 and initialized
on line 19. Then, this array of pointer is passed to
print2 function on line 20. The formal parameter of
print2 function is an array of pointers itself. The
elements of name array can now be accessed and
manipulated through a combination of subscripting and
pointer arithmetic.
The array of pointers p can also be passed to function
which receives scalar pointers to pointers like the
print3 function. Then, through subscripting, the
elements of the name array can be accessed and
manipulated.
The output of the above program is displayed below. The
output of all the three functions is the last character of the
name array, which is ‘k’.
Almost done. One important topic is remaining though.
Before proceeding to the topic, let us refer to program
3.7. In the program, the name array is declared with 11
columns and initialized with four string literals (names).
By: Asaye Chemeda Email: asayechemeda@yahoo.com 42
This means that any of the initializing string literals cannot
have more than 10 characters (the remaining one column
is for the null character). In other words, any of the
initializing names cannot have more than 10 characters.
Such arrays in which their size is defined before compile-
time are known as static arrays.
Now, assume you want to create a program which takes
names of the customers of the company that you are
working at. How many columns will you allocate for the
array which will store the names? 11 or 20 or how many?
This numbers of columns may be too small for some
names. If having small number of columns is a problem,
why not take a very large value for the column number so
that there will not be a problem when any name is taken
by the program? It seems a good solution. But, is it? If we
declare the array with a large value for the array which is
going to store names, don’t you think we are going to
waste memory space when names with small number of
characters are taken by the program?
Then, why not leave mentioning the number of columns
during declaration? That is not an option because the
compiler won’t allow us. If using small number for the
column may not store long names, if using large number
of columns wastes memory space and if leaving the
number of columns unmentioned is not an option, what
could be the best solution? The solution for this is
creating a dynamic array with dynamic array size. In
dynamic arrays, any of the array sizes are so flexible that
they can be determined after the program is compiled.
The following topic will explain it more.
Dynamic memory allocation
Throughout this chapter, we have been discussing about
pointers. Among the various concepts we discussed, we
have seen how pointers store memory addresses of
variables, how pointers are dereferenced and how
pointers can be used as arrays. In all these cases, the
pointers are useless unless there is a variable created
beforehand. The interesting question is: if the variables
can be manipulated without the use of pointers, why do
we need to define additional data?
Here is the answer. In C++ programming, memory space
is allocated for all non-pointer variables during compile-
time. However, there may be variables which only require
memory space depending on the environment during run-
time. Or variables which obtain their values during run-
time might be necessary within a program for efficiency
purposes. In such cases, pointers become powerful
options for accessing a memory space allocated during
run-time.
The pointer variables which we had been dealing with in
the previous topics obtain a memory space during
compile-time. Therefore, all non-pointer variables and the
type of pointer variables which we discussed so far obtain
the memory space allocated for them during compile
time. Such variables for which memory space is allocated
for them during compile time are known as static
variables. And those variables for which memory space
is allocated for them during run-time are known as
dynamic variables. The fact which makes pointers very
useful is that dynamic variables cannot be created without
pointers.
Then, how do we define dynamic variables? Good
question. In order to create dynamic variables in C++, a
special word is used. Dynamic variables are created in
C++ using the keyword new. If a variable is declared with
new, its memory is allocated during run-time and the
address of the allocated memory is returned. Therefore,
the variable declared by the keyword new should always
be a pointer. Note that dynamic memory space may not
always be granted.
Using new, two types of dynamic variables can be
created: a pointer with single address and an array of
pointers with multiple addresses. The syntax used to
create dynamic variables is as follows.
Dynamic single variables are declared as follows.
In the above declaration, a memory space sufficient for a
data type of dataType is allocated during run-time and
the address is returned to pointer.
For array of dynamic variables,
In the above declaration, a memory space sufficient for
Size number of elements each with a data type of
dataType is allocated during run-time and the base
address is returned to pointerArray.
After pointers or pointer arrays are created dynamically,
the elements which they point to can be accessed and
manipulated through dereferencing. Once, a variable is
defined using the keyword new, the memory space which
is allocated for the variable should be de-allocated before
using the same variable to obtain a new dynamic memory
space. Otherwise, a situation known as memory leak will
be created. This can be easily elaborated by the following
code fragment.
In the above code fragment, a double pointer p is
declared on line 1. A dynamic memory is allocated for the
dynamic variable which is going to be pointed to by p on
By: Asaye Chemeda Email: asayechemeda@yahoo.com 43
line 2. The value of the dynamic variable is initialized with
30.28 on line 3. On line 4, the pointer p again obtains a
dynamic memory space. On line 4, the value at the recent
memory location pointed by p is initialized with a value
of 38.73. Diagrammatically, this is depicted as follows.
Let us say the addresses for the memory allocated on line
2 and on line 4 are 100 and 200 respectively.
Let us focus on what happened on line 4. Even if p was
pointing to memory address 100, a new dynamic
memory, with address of 200, is allocated and the address
is returned to p. Even if the value in memory address 100
may not be used after line 4, the memory address 100,
can never be accessed. This situation is known as memory
leak. The memory size of address 100 may be few bytes.
However, thousands or millions of such leaks may result
in running out of memory space and ultimate termination
of the program.
To avoid memory leaks, dynamic memory spaces can be
de-allocated using the keyword delete. The way in
which delete is used depends on the type of the
dynamic memory to be de-allocated. To de-allocate
memory spaces allocated for single variables, the
following syntax is used.
For de-allocation of memory spaces allocated for arrays
of variables, the syntax is:
We can improve the above code fragment as follows.
The keyword delete only de-allocates the allocated
dynamic memory for future use. It has nothing to do with
the pointers or pointer arrays. After the dynamic memory
is de-allocated using delete, the pointers may still point
to the memory address they used to point. In such cases,
the pointers are said to be dangling.
To avoid the risk of dangling pointers, the pointers are
usually assigned with NULL after the memory is de-
allocated.
After our discussion on how memory is allocated
dynamically during program execution, let us see how
dynamic arrays are created in C++. It may be useful
recalling how pointers are used as arrays.
Let us upgrade program 3.7 so that a user will be
prompted to enter a name and let us store the entered
characters in a two-dimensional dynamic array called
pName. The values of the number of rows and columns
will be obtained at run-time. As a result, both these values
are variables. The number of rows will be denoted by
rows and the number of columns by columns. The
value of rows, which represents the number of names
that is going to be stored in the dynamic array, will be
entered by the customer. The value of columns, which
represents the number of characters in the names entered
by the customer, is determined from the length of the
string which stores the entered names.
To create two-dimensional dynamic arrays, we need to
define pointers to pointers. Let us denote the pointer to
pointer which will be used as a dynamic two-dimensional
array a name of pName. Note that both pName and
*pName are pointers.
To create a two-dimensional array, pName will be an
array of pointers. Each element in the array of pointers
will be an array name to an array of characters. The array
of characters will be the characters in the names entered
by the customer. First, pName will be defined to be an
array of pointers with dynamically allocated memory
space.
Each element of the pName array can be accessed and
initialized through subscripting. Note that the value of
rows in the above declaration will be determined during
program execution. Therefore, the number of names that
the dynamic array is going to store will be determined at
run time? Isn’t this an elegant way of optimizing your
memory space? Of course it is. More is yet to come. The
number of columns will also be made to be dynamic.
If i represents a row number, memory is allocated
dynamically for each column in the row under
consideration by using the following statement.
By: Asaye Chemeda Email: asayechemeda@yahoo.com 44
After this definition, pName will become a dynamic two-
dimensional array and its elements can be accessed and
manipulated through indexing.
During our discussion in chapter two, we said that
functions cannot return arrays. However, this will not be
a problem anymore. Can you guess why? Because we have
pointers. Even though functions cannot return arrays,
they can return pointers. Obviously, pointers can act as
arrays. The logic is simple now. If functions can return
pointers and if pointers can act as arrays, why don’t we
return arrays as pointers? You see another importance of
pointers. In general, if a function has to return an array, a
pointer can substitute the array and the pointer can be
returned as an array.
Let us incorporate what we discussed about dynamic
arrays and returning arrays as pointers in a program which
improves the drawbacks that could happen if we use static
arrays. Here is the program.
Program 3.8[Correct].cpp
In the above program, there are three functions including
main function. The values of the variables rows and
columns are obtained at run-time but the variables are
made global so that every function can access them.
In the main function, a pointer to pointer pName is
defined on line 8. The pointer will be used to create the
dynamic array. On line 9, the customer is prompted to
enter the number of names that he wants to enter. This is
just to show the dynamic nature of the array which we are
going to create by entering different numbers of names
for the dynamic array to hold. The number which will be
entered by the user will be the number of rows in the
array. Therefore, it will be stored in the rows variable.
The purpose of the statement in line 11 will be explained
in the next chapter. On line 12, a dynamic memory is
allocated for array of char pointers and the base address
is returned to pName. Each element in the array of
pointers will be used point to the base address of the
characters in a single name. Therefore, the number of
elements in this array of pointers will be equal to rows
and the assignment to pName was made as such.
On line 13, a function named print is called by passing
pName as an argument. The formal parameter of the
print function is also made to conform to the data type
of the passed argument. Within print function, a
pointer to pointer is defined and it is initialized by the
pointer returned by read function.
The read function takes a pointer to a pointer of char
data type as a parameter. It is the read function which
reads the names entered by the customer and stores them
in the dynamic array accordingly. Note that, since the
read function returns a pointer to pointer, the return
type of the function is stated as such. In the read
function, a string variable sName is declared on line 18.
This variable takes the name of one customer. There are
two-four loops within the function. The outer for-loop
loops across the rows while the inner for-loop loops
across all the characters in a given name. Within the outer
for-loop, the customer is prompted to enter his name on
line 20. The statement on line 21, which will be explained
in detail in the next chapter, reads all the characters in a
string entered by the customer until the ‘Enter’ key is
pressed. The entered string will be stored in the sName
variable.
Here comes another dynamic nature of the array which
we are going to create. The number of columns in a given
row will be determined after determining the number of
characters in the entered name. Isn’t this what we wanted
to improve in program 3.7? The number of characters in
By: Asaye Chemeda Email: asayechemeda@yahoo.com 45
the entered name is determined by determining the length
of sName as it is done on line 22. The determined
number of characters plus one space for the null character
is then assigned to the columns variable. This means
that the value of the columns variable is determined at
run-time. On line 23, a dynamic memory is allocated for
an array having number of elements equal to columns.
This dynamic array will be the dynamic column for each
row of the two-dimensional dynamic array. You see,
entering long names is not a problem anymore and no
memory space is going to be wasted. The exact memory
requirement will be allocated for any name entered.
On the same line of the program, i.e. line 23, the array of
pointers in name variable, which also refers to the same
memory address as pName, is assigned with the base
address of the dynamic memory allocated for each
column with the help of the array subscripting operator
[]. For instance, pName[i] contains the base memory
address of the array of characters which stores the ith
name entered by the customer.
After this, each column in the given row is assigned with
the corresponding character in the sName string on line
25. The sName string changes every time the customer
enters a name and presses the ‘Enter’ key. On line 26, the
last character in each row is assigned with null character.
Remember that on line 22, one is added to the length of
sName in the assignment to columns to accommodate
the null character. After all names are entered and stored
in the dynamic array, the pointer to pointer name is
returned by the read function. This returned pointer to
pointer is then used to assign another pointer to pointer
defined in the print function. Through this pointer to
pointer, all the names stored in the dynamic array can be
accessed by indexing. A four loop and a while-loop inside
the print function will print every character in the
entered names until a null character is encountered. How
dynamic was that?
There could also be other easier ways by which a program
with the same goal as program 3.8 can be written.
However, the program was also intended to show the
implementation of our recent discussion on dynamic
arrays and returning arrays as pointers. Indeed, the
program created a dynamic array which improves the
efficiency of memory allocation.
That is all for this chapter. It was relatively longer than the
other chapters. But, wasn’t it worth? Since pointers are
areas in C++ which can lead even a professional
programmer to write faulty programs which are difficult
to bug, the time and the effort we spent in this chapter
will be rewarded. Besides, pointers provide us the means
by which we can manipulate data in a program which
other approaches may not be capable of. So, the devotion
we put in this chapter is justified.
Even if this and the previous chapters covered lots of
basic components in C++ programming, wring an
efficient program may ask a lot more. As we have
progressed from chapter to chapter, we have gathered
more and more knowledge that we need to have as a
programmer. So, do you want to know more? I know your
answer is also yes because ‘C++ is fun’. Isn’t it? Let us go
to the next chapter then.

More Related Content

What's hot

Getting started with c++.pptx
Getting started with c++.pptxGetting started with c++.pptx
Getting started with c++.pptx
Akash Baruah
 
Data types in C
Data types in CData types in C
Data types in C
Tarun Sharma
 
Concept of c data types
Concept of c data typesConcept of c data types
Concept of c data typesManisha Keim
 
STRUCTURE AND UNION IN C MRS.SOWMYA JYOTHI.pdf
STRUCTURE AND UNION IN C MRS.SOWMYA JYOTHI.pdfSTRUCTURE AND UNION IN C MRS.SOWMYA JYOTHI.pdf
STRUCTURE AND UNION IN C MRS.SOWMYA JYOTHI.pdf
SowmyaJyothi3
 
constants, variables and datatypes in C
constants, variables and datatypes in Cconstants, variables and datatypes in C
constants, variables and datatypes in CSahithi Naraparaju
 
Lab6: I/O and Arrays
Lab6: I/O and ArraysLab6: I/O and Arrays
Lab6: I/O and Arrays
enidcruz
 
Data Type in C Programming
Data Type in C ProgrammingData Type in C Programming
Data Type in C Programming
Qazi Shahzad Ali
 
Programming Fundamentals
Programming FundamentalsProgramming Fundamentals
Programming Fundamentals
Hassan293
 
Module 1:Introduction
Module 1:IntroductionModule 1:Introduction
Module 1:Introduction
nikshaikh786
 
[ITP - Lecture 04] Variables and Constants in C/C++
[ITP - Lecture 04] Variables and Constants in C/C++[ITP - Lecture 04] Variables and Constants in C/C++
[ITP - Lecture 04] Variables and Constants in C/C++
Muhammad Hammad Waseem
 
2 expressions (ppt-2) in C++
2 expressions (ppt-2) in C++2 expressions (ppt-2) in C++
2 expressions (ppt-2) in C++
Kuntal Bhowmick
 
Module 3-Functions
Module 3-FunctionsModule 3-Functions
Module 3-Functions
nikshaikh786
 
C data types, arrays and structs
C data types, arrays and structsC data types, arrays and structs
C data types, arrays and structs
Saad Sheikh
 
Computer data type and Terminologies
Computer data type and Terminologies Computer data type and Terminologies
Computer data type and Terminologies
glyvive
 
Memory management of datatypes
Memory management of datatypesMemory management of datatypes
Memory management of datatypes
Monika Sanghani
 
Datatype in c++ unit 3 -topic 2
Datatype in c++ unit 3 -topic 2Datatype in c++ unit 3 -topic 2
Datatype in c++ unit 3 -topic 2
MOHIT TOMAR
 

What's hot (19)

Getting started with c++.pptx
Getting started with c++.pptxGetting started with c++.pptx
Getting started with c++.pptx
 
Data types in C
Data types in CData types in C
Data types in C
 
Concept of c data types
Concept of c data typesConcept of c data types
Concept of c data types
 
Ch13
Ch13Ch13
Ch13
 
STRUCTURE AND UNION IN C MRS.SOWMYA JYOTHI.pdf
STRUCTURE AND UNION IN C MRS.SOWMYA JYOTHI.pdfSTRUCTURE AND UNION IN C MRS.SOWMYA JYOTHI.pdf
STRUCTURE AND UNION IN C MRS.SOWMYA JYOTHI.pdf
 
constants, variables and datatypes in C
constants, variables and datatypes in Cconstants, variables and datatypes in C
constants, variables and datatypes in C
 
Lab6: I/O and Arrays
Lab6: I/O and ArraysLab6: I/O and Arrays
Lab6: I/O and Arrays
 
Data types
Data typesData types
Data types
 
Data Type in C Programming
Data Type in C ProgrammingData Type in C Programming
Data Type in C Programming
 
Programming Fundamentals
Programming FundamentalsProgramming Fundamentals
Programming Fundamentals
 
Module 1:Introduction
Module 1:IntroductionModule 1:Introduction
Module 1:Introduction
 
C++ data types
C++ data typesC++ data types
C++ data types
 
[ITP - Lecture 04] Variables and Constants in C/C++
[ITP - Lecture 04] Variables and Constants in C/C++[ITP - Lecture 04] Variables and Constants in C/C++
[ITP - Lecture 04] Variables and Constants in C/C++
 
2 expressions (ppt-2) in C++
2 expressions (ppt-2) in C++2 expressions (ppt-2) in C++
2 expressions (ppt-2) in C++
 
Module 3-Functions
Module 3-FunctionsModule 3-Functions
Module 3-Functions
 
C data types, arrays and structs
C data types, arrays and structsC data types, arrays and structs
C data types, arrays and structs
 
Computer data type and Terminologies
Computer data type and Terminologies Computer data type and Terminologies
Computer data type and Terminologies
 
Memory management of datatypes
Memory management of datatypesMemory management of datatypes
Memory management of datatypes
 
Datatype in c++ unit 3 -topic 2
Datatype in c++ unit 3 -topic 2Datatype in c++ unit 3 -topic 2
Datatype in c++ unit 3 -topic 2
 

Similar to Pointers in c++

C pointer
C pointerC pointer
Pointers in c language
Pointers in c languagePointers in c language
Pointers in c language
Tanmay Modi
 
Pointers in c v5 12102017 1
Pointers in c v5 12102017 1Pointers in c v5 12102017 1
Pointers in c v5 12102017 1
tanmaymodi4
 
c++ arrays and pointers grade 9 STEP curriculum.pptx
c++ arrays and pointers grade 9 STEP curriculum.pptxc++ arrays and pointers grade 9 STEP curriculum.pptx
c++ arrays and pointers grade 9 STEP curriculum.pptx
JanineCallangan
 
C pointers and references
C pointers and referencesC pointers and references
C pointers and references
Thesis Scientist Private Limited
 
Pointers in c++
Pointers in c++Pointers in c++
Pointers in c++
sai tarlekar
 
Pointers
PointersPointers
Pointers
Prasadu Peddi
 
Pointers
PointersPointers
Pointers-Computer programming
Pointers-Computer programmingPointers-Computer programming
Pointers-Computer programming
nmahi96
 
Pointers
PointersPointers
Pointers
Swarup Boro
 
cprogrammingpointerstype.ppt
cprogrammingpointerstype.pptcprogrammingpointerstype.ppt
cprogrammingpointerstype.ppt
akila m
 
Embedded C The IoT Academy
Embedded C The IoT AcademyEmbedded C The IoT Academy
Embedded C The IoT Academy
The IOT Academy
 
PSPC--UNIT-5.pdf
PSPC--UNIT-5.pdfPSPC--UNIT-5.pdf
PSPC--UNIT-5.pdf
ArshiniGubbala3
 
chapter-11-pointers.pdf
chapter-11-pointers.pdfchapter-11-pointers.pdf
chapter-11-pointers.pdf
study material
 
C Programming Unit-4
C Programming Unit-4C Programming Unit-4
C Programming Unit-4
Vikram Nandini
 
C UNIT-3 PREPARED BY M V B REDDY
C UNIT-3 PREPARED BY M V B REDDYC UNIT-3 PREPARED BY M V B REDDY
C UNIT-3 PREPARED BY M V B REDDYRajeshkumar Reddy
 
C++ Pointers with Examples.docx
C++ Pointers with Examples.docxC++ Pointers with Examples.docx
C++ Pointers with Examples.docx
JoeyDelaCruz22
 
Pointers in C
Pointers in CPointers in C
Pointers in C
Monishkanungo
 

Similar to Pointers in c++ (20)

C pointer
C pointerC pointer
C pointer
 
Pointers in c language
Pointers in c languagePointers in c language
Pointers in c language
 
Pointers in c v5 12102017 1
Pointers in c v5 12102017 1Pointers in c v5 12102017 1
Pointers in c v5 12102017 1
 
c++ arrays and pointers grade 9 STEP curriculum.pptx
c++ arrays and pointers grade 9 STEP curriculum.pptxc++ arrays and pointers grade 9 STEP curriculum.pptx
c++ arrays and pointers grade 9 STEP curriculum.pptx
 
C pointers and references
C pointers and referencesC pointers and references
C pointers and references
 
Pointers in c++
Pointers in c++Pointers in c++
Pointers in c++
 
Pointers
PointersPointers
Pointers
 
Pointers
PointersPointers
Pointers
 
Pointers-Computer programming
Pointers-Computer programmingPointers-Computer programming
Pointers-Computer programming
 
Lect 9(pointers) Zaheer Abbas
Lect 9(pointers) Zaheer AbbasLect 9(pointers) Zaheer Abbas
Lect 9(pointers) Zaheer Abbas
 
Pointers
PointersPointers
Pointers
 
cprogrammingpointerstype.ppt
cprogrammingpointerstype.pptcprogrammingpointerstype.ppt
cprogrammingpointerstype.ppt
 
Lect 8(pointers) Zaheer Abbas
Lect 8(pointers) Zaheer AbbasLect 8(pointers) Zaheer Abbas
Lect 8(pointers) Zaheer Abbas
 
Embedded C The IoT Academy
Embedded C The IoT AcademyEmbedded C The IoT Academy
Embedded C The IoT Academy
 
PSPC--UNIT-5.pdf
PSPC--UNIT-5.pdfPSPC--UNIT-5.pdf
PSPC--UNIT-5.pdf
 
chapter-11-pointers.pdf
chapter-11-pointers.pdfchapter-11-pointers.pdf
chapter-11-pointers.pdf
 
C Programming Unit-4
C Programming Unit-4C Programming Unit-4
C Programming Unit-4
 
C UNIT-3 PREPARED BY M V B REDDY
C UNIT-3 PREPARED BY M V B REDDYC UNIT-3 PREPARED BY M V B REDDY
C UNIT-3 PREPARED BY M V B REDDY
 
C++ Pointers with Examples.docx
C++ Pointers with Examples.docxC++ Pointers with Examples.docx
C++ Pointers with Examples.docx
 
Pointers in C
Pointers in CPointers in C
Pointers in C
 

Recently uploaded

Cyaniclab : Software Development Agency Portfolio.pdf
Cyaniclab : Software Development Agency Portfolio.pdfCyaniclab : Software Development Agency Portfolio.pdf
Cyaniclab : Software Development Agency Portfolio.pdf
Cyanic lab
 
Enhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdfEnhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdf
Globus
 
2024 RoOUG Security model for the cloud.pptx
2024 RoOUG Security model for the cloud.pptx2024 RoOUG Security model for the cloud.pptx
2024 RoOUG Security model for the cloud.pptx
Georgi Kodinov
 
First Steps with Globus Compute Multi-User Endpoints
First Steps with Globus Compute Multi-User EndpointsFirst Steps with Globus Compute Multi-User Endpoints
First Steps with Globus Compute Multi-User Endpoints
Globus
 
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERRORTROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
Tier1 app
 
GlobusWorld 2024 Opening Keynote session
GlobusWorld 2024 Opening Keynote sessionGlobusWorld 2024 Opening Keynote session
GlobusWorld 2024 Opening Keynote session
Globus
 
Quarkus Hidden and Forbidden Extensions
Quarkus Hidden and Forbidden ExtensionsQuarkus Hidden and Forbidden Extensions
Quarkus Hidden and Forbidden Extensions
Max Andersen
 
How to Position Your Globus Data Portal for Success Ten Good Practices
How to Position Your Globus Data Portal for Success Ten Good PracticesHow to Position Your Globus Data Portal for Success Ten Good Practices
How to Position Your Globus Data Portal for Success Ten Good Practices
Globus
 
Top Features to Include in Your Winzo Clone App for Business Growth (4).pptx
Top Features to Include in Your Winzo Clone App for Business Growth (4).pptxTop Features to Include in Your Winzo Clone App for Business Growth (4).pptx
Top Features to Include in Your Winzo Clone App for Business Growth (4).pptx
rickgrimesss22
 
Enterprise Resource Planning System in Telangana
Enterprise Resource Planning System in TelanganaEnterprise Resource Planning System in Telangana
Enterprise Resource Planning System in Telangana
NYGGS Automation Suite
 
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Anthony Dahanne
 
Accelerate Enterprise Software Engineering with Platformless
Accelerate Enterprise Software Engineering with PlatformlessAccelerate Enterprise Software Engineering with Platformless
Accelerate Enterprise Software Engineering with Platformless
WSO2
 
Cracking the code review at SpringIO 2024
Cracking the code review at SpringIO 2024Cracking the code review at SpringIO 2024
Cracking the code review at SpringIO 2024
Paco van Beckhoven
 
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
Globus
 
BoxLang: Review our Visionary Licenses of 2024
BoxLang: Review our Visionary Licenses of 2024BoxLang: Review our Visionary Licenses of 2024
BoxLang: Review our Visionary Licenses of 2024
Ortus Solutions, Corp
 
Providing Globus Services to Users of JASMIN for Environmental Data Analysis
Providing Globus Services to Users of JASMIN for Environmental Data AnalysisProviding Globus Services to Users of JASMIN for Environmental Data Analysis
Providing Globus Services to Users of JASMIN for Environmental Data Analysis
Globus
 
Globus Compute wth IRI Workflows - GlobusWorld 2024
Globus Compute wth IRI Workflows - GlobusWorld 2024Globus Compute wth IRI Workflows - GlobusWorld 2024
Globus Compute wth IRI Workflows - GlobusWorld 2024
Globus
 
Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...
Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...
Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...
Globus
 
How Recreation Management Software Can Streamline Your Operations.pptx
How Recreation Management Software Can Streamline Your Operations.pptxHow Recreation Management Software Can Streamline Your Operations.pptx
How Recreation Management Software Can Streamline Your Operations.pptx
wottaspaceseo
 
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.ILBeyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Natan Silnitsky
 

Recently uploaded (20)

Cyaniclab : Software Development Agency Portfolio.pdf
Cyaniclab : Software Development Agency Portfolio.pdfCyaniclab : Software Development Agency Portfolio.pdf
Cyaniclab : Software Development Agency Portfolio.pdf
 
Enhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdfEnhancing Research Orchestration Capabilities at ORNL.pdf
Enhancing Research Orchestration Capabilities at ORNL.pdf
 
2024 RoOUG Security model for the cloud.pptx
2024 RoOUG Security model for the cloud.pptx2024 RoOUG Security model for the cloud.pptx
2024 RoOUG Security model for the cloud.pptx
 
First Steps with Globus Compute Multi-User Endpoints
First Steps with Globus Compute Multi-User EndpointsFirst Steps with Globus Compute Multi-User Endpoints
First Steps with Globus Compute Multi-User Endpoints
 
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERRORTROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
TROUBLESHOOTING 9 TYPES OF OUTOFMEMORYERROR
 
GlobusWorld 2024 Opening Keynote session
GlobusWorld 2024 Opening Keynote sessionGlobusWorld 2024 Opening Keynote session
GlobusWorld 2024 Opening Keynote session
 
Quarkus Hidden and Forbidden Extensions
Quarkus Hidden and Forbidden ExtensionsQuarkus Hidden and Forbidden Extensions
Quarkus Hidden and Forbidden Extensions
 
How to Position Your Globus Data Portal for Success Ten Good Practices
How to Position Your Globus Data Portal for Success Ten Good PracticesHow to Position Your Globus Data Portal for Success Ten Good Practices
How to Position Your Globus Data Portal for Success Ten Good Practices
 
Top Features to Include in Your Winzo Clone App for Business Growth (4).pptx
Top Features to Include in Your Winzo Clone App for Business Growth (4).pptxTop Features to Include in Your Winzo Clone App for Business Growth (4).pptx
Top Features to Include in Your Winzo Clone App for Business Growth (4).pptx
 
Enterprise Resource Planning System in Telangana
Enterprise Resource Planning System in TelanganaEnterprise Resource Planning System in Telangana
Enterprise Resource Planning System in Telangana
 
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
 
Accelerate Enterprise Software Engineering with Platformless
Accelerate Enterprise Software Engineering with PlatformlessAccelerate Enterprise Software Engineering with Platformless
Accelerate Enterprise Software Engineering with Platformless
 
Cracking the code review at SpringIO 2024
Cracking the code review at SpringIO 2024Cracking the code review at SpringIO 2024
Cracking the code review at SpringIO 2024
 
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
Climate Science Flows: Enabling Petabyte-Scale Climate Analysis with the Eart...
 
BoxLang: Review our Visionary Licenses of 2024
BoxLang: Review our Visionary Licenses of 2024BoxLang: Review our Visionary Licenses of 2024
BoxLang: Review our Visionary Licenses of 2024
 
Providing Globus Services to Users of JASMIN for Environmental Data Analysis
Providing Globus Services to Users of JASMIN for Environmental Data AnalysisProviding Globus Services to Users of JASMIN for Environmental Data Analysis
Providing Globus Services to Users of JASMIN for Environmental Data Analysis
 
Globus Compute wth IRI Workflows - GlobusWorld 2024
Globus Compute wth IRI Workflows - GlobusWorld 2024Globus Compute wth IRI Workflows - GlobusWorld 2024
Globus Compute wth IRI Workflows - GlobusWorld 2024
 
Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...
Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...
Exploring Innovations in Data Repository Solutions - Insights from the U.S. G...
 
How Recreation Management Software Can Streamline Your Operations.pptx
How Recreation Management Software Can Streamline Your Operations.pptxHow Recreation Management Software Can Streamline Your Operations.pptx
How Recreation Management Software Can Streamline Your Operations.pptx
 
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.ILBeyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
Beyond Event Sourcing - Embracing CRUD for Wix Platform - Java.IL
 

Pointers in c++

  • 1. By: Asaye Chemeda Email: asayechemeda@yahoo.com 26 CHAPTER THREE POINTERS Introduction The data types in C++ are broadly categorized into simple data types, structured data types and pointers. We have already seen the first two. In this chapter, we will deal about the third categories of data types in C++, i.e., pointers. Their name is coined from the fact that these data types point to another data. Pointer variables, or shortly pointers, are data types which are derived based on other data types. There is also no name associated with the data type of pointers. However, they are considered to be a separate data types in C++. Pointer variables are used to store memory addresses of the variables they point to. The variables which are pointed to by pointers have a name for their data types, unless they are pointers themselves. Even if the data type of pointers has no name in C++, pointers are usually considered to have the same data type as the variable they point to and are defined as such. However, there are also pointers called generic pointers with void data type. These types of pointers can point to any variable with any data type. They can also be assigned with other pointers which point to any data type. Since pointers refer to the memory addresses of other variables, they are very powerful means for manipulation of data in C++. Pointers will provide you the way by which you can manipulate data which other conventional approaches fail to do. Thus, they are very useful tools in C++. However, they may also have undesirable consequences unless they are handled carefully in a program. Defining Pointers Pointers take the same data type but different content than the variables which they point to. If so, how are these pointer variables are defined? What makes them be distinguished from non-pointer variables? Pointer variables are distinguished from other non- pointer variables by using the asterisk symbol, ‘*’, during their definition. If the asterisk symbol is put in front of a variable during its declaration, the compiler will consider it to be a pointer. The asterisk symbol can appear anywhere between the data type and the pointer variable. However, it is a good practice to pre-attach it with the pointer variable. The syntax used to define a pointer variable is: In the above declaration, the dataType , which can also be void, is usually the same as the data type of the variable that the pointerVariable points to. Once a pointer variable is defined, how it is then used for data manipulation? Before answering this question, we will discuss about two operators which are often used with pointers. They are the address of operator denoted by the ampersand symbol, ‘&’ and the dereferencing or at address or indirection operator denoted by the asterisk symbol,’*’. The address of operator returns the memory address of its operand while the dereferencing operator returns the value stored at a given memory location. To understand this a bit more, let us see the following piece of code. In the above code, an integer variable x is defined and initialized with a value of 100 on line 1. As you know, when values are declared, a memory space is allocated for the variables during compile time. Those memory locations have addresses. Let us say that the memory address where x is stored, amongst others, is 1000. Let us depict this diagrammatically as follows. The above diagram shows that at memory location 1000, an integer value of 100 is stored. On line 2, a pointer variable p is declared by using the * symbol. Which means that the variable p will store memory address of another variable, i.e., p is declared to point to another variable. The int data type that the pointer variable is declared by indicates that the pointer will point to a variable with int data type. Again, a separate memory location will be allocated for this variable. Let us say the memory location allocated for p has an address of 1003. When we depict this graphically, as in the above diagram, we will have the following. The above diagram shows that a memory address of 1003 is allocated for the additional variable p. Note that, the pointer variable is not yet initialized and at this stage it can point to any memory location. If your program tries to
  • 2. By: Asaye Chemeda Email: asayechemeda@yahoo.com 27 access memory address 1003 which is not yet initialized and use the address which it points to now, your program may be directed to any memory location even those having sensitive system data and your computer system may crash. Line 3 shows how a pointer variable is initialized. On line 3, p is assigned with the address of variable x by using the address of operator &. The part on the right hand side of the assignment operator ( = ), ‘&x’, returns the memory address of variable x. The assignment operator will assign this address to the pointer variable p on the left hand side. Which means that the memory address of variable x, which is 1000, will be stored in the memory address of p, which is 1003. In short, p is now pointing to variable x. After this, the memory locations will appear to be: In the above diagram, the memory address 1003 now contains another memory address 1000, which is the memory address of variable x. After the initialization on line 3, the variable p points to a specific known address and you can use it without any harm to your computer. On line 4, the value at address contained by variable p is assigned with value of 25 by using the dereferencing operator *. The part on the left hand size of assignment operator of the statement, *p, refers to the value stored at memory address contained by pointer variable p. The memory address contained by p is 1000 and the value at address 1000 was 100. Therefore, the statement on line 4 changes this value to 25. Diagrammatically, what happens after line 4 is shown as follows: In the above diagram, the value at address 1000 is now changed to 25. Note that, memory address 1003 still contains the memory location of variable x and further changes can be made through the same procedure. However, the pointer variable p can also be made to point to another variable. The way how pointers manipulate data in C++ is as simple as what we discussed above. But that is not all about pointers. Based on the above concept, we can utilize pointers to access and manipulate data in different ways. Before proceeding to those details, don’t you think it is good if we can incorporate the above piece of code into a full program and see the results? If that is also your thought, let us see the following program. Program 3.1[Correct].cpp When the above program is run, the resulting output is shown below. Note that, the address results are machine dependent and you may obtain different addresses than those shown below. What we confirmed from the above output is that (1) The address of x can be obtained by either the address of operator & or by the pointer variable p which points to the variable x (Line 9 of the program and line 1 of the output). (2) A memory location is also allocated for the pointer variable p which can be obtained by the & operator (Line 11 of the program and line 2 of the output). (3) The value of the variable x can be obtained from the variable itself or from its pointer variable p by using the dereferencing operator *. (Line 13 of the program and line 3 of the output). (4) The value of the variable x can be changed by using the * operator and its pointer variable p. (Line 15 of the program and line 4 of the output). Have you noticed that pointers are variables themselves? Their difference from other non-pointer variables are due to the type of data they store being different from other simple or structured data types. You may ask the following string of questions. If pointers are variables, can we perform arithmetic operations on them? If it is possible, what do the results of arithmetic operations on pointers refer to? Do the results even have any real purpose in programming? All these questions will be answered in the following topic. Pointer Arithmetic Besides being assigned with and compared to another pointer variable, only the following arithmetic operations can be performed on pointers. 1. Pointer variables can be incremented or decremented.
  • 3. By: Asaye Chemeda Email: asayechemeda@yahoo.com 28 2. Integer values can be added or subtracted from pointers. 3. One pointer variable can be deducted from another pointer variable. Note that any other arithmetic operation is prohibited on pointers. Understanding the above arithmetic operations to a modest detail is helpful and they will be discussed below. Assigning pointer with another pointer or a value. A pointer is not only assigned or initialized with the help of the & operator. A pointer can also be assigned or initialized with another pointer. After the assignment, both the assigned and the assigning pointers refer to the same memory location. Let us have a look at the following piece of code. On line 1, a character variable c is declared and initialized with a character literal ‘A’. On line 2, pointers p1 and p2 are declared. On line 3, p1 is initialized with the address of c. On line 4, p2 is assigned with p1. After line 4, both p1 and p2 will point to c. What each line of the above piece of code will do is graphically depicted as below. Note that the memory addresses are arbitrary. Pointers of any data type can also be assigned with only one value, i.e. ‘0’. Pointers which are assigned with this value are called null pointers and they point to nothing. Null pointers can also be created by assigning pointers of any data type with the word ‘NULL’. Null pointers are conventionally used to avoid problems caused by uninitialized pointers. Comparison of pointer variables. A comparison can be made between two pointers. In general, pointers can be used in logical comparison statements. These comparisons, however, are based on the equality or otherwise of the addresses contained by pointers. For instance, if p and q are two pointers, • p == q returns true if the memory address in p is equal to that in q or false otherwise. • p < q returns true if the memory address in p is less than that in q or false otherwise. • p > q returns true if the memory address in p is greater than that in q or false otherwise. Such pointer comparisons might be useful if we are interested to know whether two pointers refer to the same memory address or not. However, the purpose of pointer comparison is not limited to this and you can use the concept whenever appropriate. Incrementing or decrementing of pointers. The values of pointers can be incremented by the ++ operator or decremented by the -- operator. What the incrementing or decrementing operations will do the pointer variable is interesting. When a pointer is incremented or decremented, the value by which the pointer will do as such depends on the data type of the pointer. For character pointers which point to characters which always have 1byte size, an increment adds one to the pointer where as a decrement subtracts one from the pointer. For integer pointers which point to int variables which normally have 4byte size, an increment adds four to the pointer where as a decrement subtracts four from the pointer. In short, the increment or decrement of pointers is done by an amount equal to the memory size allocated for a single base data type. Usually, memory addresses correspond to hexadecimal numbers. However, for simplicity purposes, we will use memory addresses with integer values and we will see how incrementing and decrementing is done for pointers which point to different data types. Since the procedure is similar for different data types, we will only deal with char and int data types. Increment and decrement in char pointers Let us refer to the following piece of code. On line 1 of the above of code, character variables c1, c2 and c3 are declared and initialized with character literals ‘A’, ‘B’ and ‘C’ respectively. On line 2, a pointer pChar with char data type is declared and initialized with address of c1. At this point, pChar points to c1. Let us assume that c1, c2 and c3 are stored in contiguous
  • 4. By: Asaye Chemeda Email: asayechemeda@yahoo.com 29 memory locations with addresses of 2000, 2001 and 2002 respectively and pChar is stored at memory address 2004. At this point, pChar and *pChar are equal to 2000 and ‘A’ respectively. This is depicted in a diagram as follows. When pChar is incremented on line 3, the memory address in pChar will be incremented by the amount of the memory size of a single character variable, which is 1. After the increment, the memory address in pChar becomes 2001. Which means that pChar now points to c2. Thus, pChar and *pChar are equal to 2001 and ‘B’ respectively. Diagrammatically, this is represented as: When pChar is incremented on line 4 one more time, the memory address in pChar will be incremented by 1. After the increment, the memory address in pChar becomes 2002. Which means that pChar now points to c3. Thus, pChar and *pChar are equal to 2002 and ‘C’ respectively. When this is put in diagram, we will have: When pChar is decremented on line 5, the memory address in pChar will be decremented by 1 and becomes 2001. Which means that pChar now points to c2 again. Thus, pChar and *pChar are equal to 2001 and ‘B’ respectively. When this is put in diagram, we will have: Increment and decrement in int pointers Let us refer to the following piece of code. On line 1 of the above of code, int variables i1, i2 and i3 are declared and initialized with integer values ‘75’, ‘83’ and ‘49’ respectively. On line 2, a pointer pInt with int data type is declared and initialized with address of i1. At this point, pInt points to i1. Since the memory size of int data types is normally 4bytes, each integer variable requires four memory cells. Let us assume that i1, i2 and i3 are stored in contiguous memory locations with the first cell addresses of 10, 14 and 18 respectively and pInt is stored at memory address 26. At this point, pInt and *pInt are equal to 10 and 75 respectively. This is depicted in a diagram as follows. The memory addresses are written for alternate cells to avoid congestion of characters. When pInt is incremented on line 3, the memory address in pInt will be incremented by the amount of the memory size of a single int variable, which is 4. After the increment, the memory address in pInt becomes 14 not 11. Which means that pInt now points to the next int variable which is i2. Thus, pInt and *pInt are equal to 14 and 83 respectively. Diagrammatically, this is represented as: When pInt is incremented on line 4 one more time, the memory address in pInt will be incremented by 4. After the increment, the memory address in pInt becomes 18. Which means that pInt now points to i3. Thus, pInt and *pInt are equal to 18 and 49 respectively. When this is put in diagram, we will have: When pInt is decremented on line 5, the memory address in pInt will be decremented by 4 and becomes 14. Which means that pInt now points to i2 again. Thus, pInt and *pInt are equal to 14 and 83 respectively. When this is put in diagram, we will have: Isn’t the way that the compiler handles incrementing and decrementing operations on pointers fascinating? Indeed
  • 5. By: Asaye Chemeda Email: asayechemeda@yahoo.com 30 it is. But, have you noted this? In the above examples we assumed that the variables are stored in contiguous (interconnected) memory locations. But, is that always the case? Not necessarily. Simple data type values may not be stored as such. As a result, it may be difficult to keep track of the memory locations for variables belonging to simple data types and trying to use the incrementing and decrementing operators to access values of such data types may result in wrong calculation results or data corruption. If so, what is the importance of incrementing and decrementing pointers? Here is the answer for the above question. Some data types are stored in the main memory in a structured manner. Structured data types such as arrays are good example for this. When elements of arrays are stored in the main memory, the compiler will allocate contiguous memory locations for them. Therefore, if the address of the first element is known, the address of the other elements can easily be tracked based on the memory size requirement for each element. As you may recall, the identifier used for defining arrays refers to the address of the first element. In other words, the identifier of an array is a pointer to the first array element. The identifier can be assigned to a pointer. Based on a pointer which is assigned with identifier of an array, the memory addresses of all the other elements can easily be obtained through incrementing and decrementing. You see, that is where the incrementing and decrementing operations on pointers become very useful. In chapter two, we have seen how arrays are passed to functions. Do you remember how it was done? It was by reference, right? After our recent discussion, do you see another way by which arrays can be passed to functions? Let me give you a hint. Just remember how the following four things are done. How value is transferred from the argument passed to the formal parameter list of the function when a function is called. How pointers are declared. What the identifier of an array refers to. And how pointer increment and decrement is done. When we revise how the above four things are done, we will get the following. When a function is called by passing arguments, the called function defines and assigns the variables in its formal parameter list with the passed arguments according to their order. Pointer variables are declared by pre-attaching * to the name of the pointer variable. The identifier of an array is a pointer to the first element of the array. Pointer incrementing and decrementing is normally done to access and manipulate data in contiguous memory locations by increment and decrement operators. Now, let us see how we can take the above knowledge to our advantage to pass arrays to functions and manipulate the elements of the array. When a function call is made to pass an array, let the calling function pass the identifier of the array. Let us make the corresponding argument on the formal parameter list of the called function to be a pointer. Which means that, during function call, the array identifier, which is a pointer, will be assigned to the corresponding pointer variable in the called function. Inside the body of the called function, all the array elements can now be accessed and manipulated by incrementing and decrementing the pointer which is assigned by the array identifier. Don’t you think it is a good strategy? Of course it is. This way of passing arrays to functions is termed as passing arrays by pointers and if the program is well-written, it is the neatest way to pass arrays. Note that, the changes made on the pointers of array elements inside the body of the function is also reflected in the calling environment. The above way of passing arrays is applicable for one-dimensional arrays. Passing two-dimensional arrays can also be done through pointers. But, let us keep discussing this concept aside until we start dealing with the ‘pointers and arrays’ topic. I feel that we have discussed lots of concepts and we need to take a break. Do you also feel the same? However, in the meantime, let us write a program through which we will be able to understand what we discussed so far about arithmetic operations on pointers as well as about passing arrays by pointers. Here is the program. Program 3.2 [Correct].cpp
  • 6. By: Asaye Chemeda Email: asayechemeda@yahoo.com 31 The above program calculates the cube roots of the first five positive perfect cubes by the cubeRoot function and prints the cube roots by the print function. We will only discuss the important points in the program. On line 11, the cubeRoot function is called and the identifier of the cube array is passed as an argument. Technically, what is passed as an argument is a pointer to the first element of the cube array. When the cubeRoot function is invoked by this call, the first thing the function is going to do is assigning its formal parameter accordingly with the passed argument. In C++ statement, this assignment is represented as: Isn’t this how a pointer is declared initialized with another pointer? Of course it is. In the above declaration, we need to understand that the data type of c is double and it can only be initialized with a pointer of only double data type. After the above declaration the pointer c will contain the memory address of the first element of the cube array. On line 17, the cube root of the value at address contained by c is calculated and stored in the same address contained by c. On line 18, the pointer is incremented. After each increment, the pointer c will point to the next element in the contiguous memory location, i.e., the next element of cube array. The for-loop controls for how many times that the increment will be done. When the loop is complete, all the elements of the cube array will now contain the cube roots of the corresponding original values. This change is also reflected in the main function where the cubeRoot function is called. Therefore, when the array identifier cube is now passed to the print function on line 12, it refers to the memory address of the first element of the changed cube array elements not those elements given on line 10. Note that the data type of the formal parameter of the print function is void. This means that, the cubeRt pointer is a void pointer which can be assigned with another pointer which points to a variable of any data type. However, to access and manipulate the data contained by the cubeRt pointer, explicit type casting should be made. Type casting is conversion of one data type into another. One way to do this in C++ is by putting the data type to be converted to within brackets in front of the variable which we want to convert its data type as it was done on line 22. However, type casting may not be applicable for conversion between some data types. Note that the casting on line 22 could also be done by using the static_cast key word and the data type to be casted to within angled brackets as in the following statement: On line 22 of the program, the void cubeRt pointer is casted or converted to double pointer and the casted pointer is assigned to a double pointer named x. Through pointer x, the elements of the array x can now be accessed and printed by using the increment operator. The part of the statement on line 24, *x++, means access the value stored at address contained by x and increment the pointer x to the next value. I hope the above example clears any doubts you might have regarding pointer incrementing and passing one- dimensional arrays to functions as pointers. Remember, we haven’t yet covered all the possible arithmetic operations on pointers; two more are remaining. Adding or subtracting integers from pointer variables. If you understand how incrementing and decrementing of pointers is done, understanding how adding or subtracting of integers is done is pretty easy. As you know, when incrementing is done by the ++ operator, the value of the variable on which the incrementing is done will be increased by sizeof(base data type of the variable). The keyword sizeof is a reserved C++ word used to return the size of the memory space allocated for objects. The value sizeof(base data type) of may not merely mean one. It may mean one for char pointers, four for int pointers or eight for double pointers. Adding an integer i to a pointer, therefore, means adding i times size of the pointer variable. The same is true for subtracting integers. Let us see this through the following piece of code. In the above piece of code, a double variable x is defined and initialized on line 1. On line 2, a double pointer p is defined and initialized with the address of x. Say the address of x is 100. After line 2, the value of p will be 100. Double data types normally require 8bytes. A single increment on pointer p by ++ will result in a value of p to be 108. However, on line 3, the value of p is increased by 10. What do you think will the value of p become after line 3 when the value of p after line 2 is 100? Are you saying 110? If you say so, you might have forgotten how incrementing of pointers is done. When 10 is added to a double data type pointer variable, the value of the pointer will be incremented by 10 times sizeof(double) (i.e., 8). In other words, when 10 is added to pointer p, the value of the pointer will be incremented by 80. Therefore after line 3, the value of p becomes 180. Subtracting integers from pointers is also done in a similar procedure.
  • 7. By: Asaye Chemeda Email: asayechemeda@yahoo.com 32 Deducting two pointer variables. Two pointers pointing to the same data type may also be deducted from each other. The difference between two pointers results in an integer value. The absolute value of the resulting integer represents the number of objects of the base data type between the pointers. The difference between two pointers, however, cannot be assigned to another pointer even if the difference is equal to zero. We have now seen the only possible arithmetic operations on pointers. The following operations, amongst others, are prohibited. • Adding two pointers • Assigning non-zero values to pointers • Adding or subtracting floating-point data type values from pointers. • Multiplying or dividing two pointers. At this point, we have discussed the basics of pointers and pointer operations. Let us proceed further and discuss other details. Have you ever imagined that there could also be array of pointers? Indeed, there are arrays of pointers too. The concept of arrays of pointers integrates the concepts of both arrays and pointers. Arrays of pointers are arrays which contain pointers as elements. They are declared and used in the same way as other arrays. The only difference is the asterisk symbol should be used to tell the compiler that the array is an array of pointers. For instance, an array of int pointers x having 10 elements can be declared as follows. What would x store if it was just an int array declared without the asterisk? Ten integer values, right? Because of the asterisk, however, x is declared as a pointer array and it stores ten memory addresses with each memory address pointing to an int variable. After this declaration, x can be initialized with memory addresses of other variables. If we want to initialize the fourth element of x with the address of an int variable y, we can write the following: Now, if we want to initialize the value of y by 76, we can use the following statement: We have discussed that pointers point to other variables which have their own data types. The interesting question here is: can pointers point to another pointers? Of course, they can. Simply, ask yourself what the name of array of pointers represents. If you understand what kind of pointer that a name of array of pointers is, reading the next topic might not be necessary. Otherwise, go through the next topic for a better understanding. Multiple Indirection Pointers are variables themselves and a memory location is allocated for them. If a pointer stores the memory location allocated for another pointer variable, the pointer is pointing to another pointer variable. In C++, the process of pointing at another pointer is known as multiple indirection. The pointer with multiple indirection is known as pointer to a pointer. The process of multiple indirection can be repeated as many as we want although more than three multiple indirection is rarely used in programming. Pointers to pointers can be defined in the same way as other pointers with only one indirection. The only difference is, as many asterisk symbols as the number of indirections will be put between the pointer and data type of the pointer. The data type of pointers with multiple indirection will be the same as the data type of the target value. A pointer with two indirections is declared with the following syntax. In the above declaration, the two asterisk symbols between the pointerVariable and dataType indicate that the pointerVariable will point to another pointer. Pointers to pointers can only be assigned with address of other pointers or ‘0’. The way how we use the address of operator (&) and the indirection operator (*) while using pointers to pointers is the same as simple pointers. The target value can also be accessed and manipulated by pointers to pointer by using as many indirection operators as they are used to define the pointer to pointer. It is easy, right? If not, the following simple program will make it so. Program 3.3[Correct].cpp
  • 8. By: Asaye Chemeda Email: asayechemeda@yahoo.com 33 In the above program, a float variable x is defined and initialized on line 6. A pointer p and a pointer to pointer pTp are defined on line 7. Note that, if p is supposed to point to x and if pTp is supposed to point to p, the data type of pTp will also be the same as the data type of x. that is the reason why p and pTp were defined as such. On line 8, the address of variable x is assigned to p and on line 9, the address of p is assigned to pTp. Note that, trying to assign pTp with address of a non-pointer variable such as x rather than with address of a pointer would have resulted in compilation error. From the above program, the main expected outcome is that the output of line 10 and line 13 to be the same. If you understand why this should be, it means that you have understood multiple indirection. If not, there should be no worries as we will go through the output of the five cout statements. The output of the statement on line 10 is the address of the variable x contained in p. Since p is assigned with the address of p on line 8, displaying the value of p by cout statement will result in displaying the memory address of x. The value of pTp will be displayed by the cout statement on line 11. The output of this line is the memory address of p contained in pTp. This is because pTp is assigned with the address of p on line 9. Through the indirection operator, the value at address contained in p will be displayed as an output of line 12. What is address contained in p? It is the address of variable x, right? Therefore, *p access the value of x. When we come to the statement on line 13, a value referred by *pTp will be printed. This means the value at address contained by pTp will be displayed. On line 9, pTp is assigned with the address of p. Therefore, pTp contains the address of p and *pTp accesses the value at the address contained by pTp which is the value of p which is also the address of x. Therefore, *pTp and p refer to the same thing and the output of line 10 and line 13 will be the same. The value of x can also be accessed through pTp by applying the same number of indirection operators as it was used to define pTp. The cout statement on line 14, which prints the value of **pTp, actually prints the value of x. We have seen above that when the * is pre-attached once on pTp, it accesses p, when another one is pre- attached, the value of x will be accessed. Although the memory addresses are machine dependent, the output of the above program is shown below. As can be observed, the memory addresses printed on the first and forth lines of the output are the same which asserts our explanation above. I guess you have mixed feelings about multiple indirections. On one hand, you may wonder how pointers to pointers work to access and manipulate values. One the other hand, you may be curious about what the purpose of defining pointers to pointers is. Am I right? If so, my suggestion is to keep on your wondering about multiple indirections. And the following topic will answer your curiosity. Pointers and Arrays In C++, pointers and arrays are interrelated. The second chapter on arrays and our discussion so far in this chapter about pointers have provided us the subtle differences between them. In C++ programming, pointers are often used to access and manipulate array elements. Arrays can also be used as pointers. In our discussion about arrays in chapter two, we have said that array names refer to the memory locations of the first array elements. Therefore, array names are pointers themselves. Even if array names are pointers, you can’t perform pointer arithmetic on them which attempts to change their values. In general, array names are constant pointers. You may ask, in what way are arrays and pointers are interrelated? We have already seen how one-dimensional arrays and pointers can be interrelated during our discussion on incrementing and decrementing pointers. The concepts which were not discussed then, will be covered in this topic. Let us start discussing more about the relationship between arrays and pointers for one dimensional arrays. Consider the following one-dimensional array declaration: In the above array the name numbers is a pointer to the first array element, i.e. it contains the memory address of the value 10. Through indexing, the first element is accessed by numbers[0]. Therefore, the following logical statement returns true.
  • 9. By: Asaye Chemeda Email: asayechemeda@yahoo.com 34 The above logical statement compares the equality between numbers and the address of numbers[0]. Since the returned value is true, it means that numbers contains the address of numbers[0]. Thus, numbers is a pointer. If numbers is dereferenced, it gives the value of the first array element. The following logical statement will, therefore, return true. The first array element can also be changed to another int value, say 15, by using the array name through the following statement. So far so good. The array name, numbers, is behaving exactly the same as other regular pointers. Can we conclude that array name can be used as like any other pointer? Before rushing to the conclusion, let us apply some arithmetic operations on numbers and let us see how it behaves. First, let us apply the incrementing operation on numbers and let us point to the next element of the array. Recall that incrementing pointers will make them point to the next element in contiguous memory locations. Trying to point to the next element of the numbers array through the following statement, however, results in compilation error. Array names are not like other pointers, after all. Can you guess what the reason for the compilation error could be? It is the ++ operator. Since the data type of the array is int, the above statement attempted to increment the value of numbers by sizeof(int). But, array names are constant pointers and their values cannot be changed and they only point to the first array element. This rule is not limited for only incrementing, any other arithmetic operation which attempts to change the value of an array name is illegal. We can now conclude that array names are pointers with restrictions. If incrementing and decrementing is illegal on array names, we may think that, they are useless to point to other array elements than the first one. However, that is not true at all. Array names can also be used to point to all array elements. The reason why the above statement failed to point to the next array element is because the ++ operator attempted to change the value of numbers, which is a constant pointer. Any valid arithmetic operation which doesn’t attempt to change the value of numbers can point to other elements of the array as well. Let us modify the above increment statement by this one: Both the statements on Line 1 and Line 2 add sizeof(int) to the value of numbers. The only difference between them is, the statement on Line 2 doesn’t attempt to change the value of numbers while the statement on Line 1 does. Therefore, the statement on Line 2 is legal and it points to the second element of numbers array. If we dereference it, the second element of numbers array will be returned. For instance, the following statement prints the value of the second element of the array: And the following statement changes the value of the fourth element of the array to 32. By using the name of the array, we accessed and manipulated the array elements which we wanted. It was fascinating, right? The above example shows how one- dimensional arrays can be used as pointers and the associated restrictions while doing so. Can we say that one-dimensional arrays and pointers are the same except the arithmetic restrictions on array names? Not really. There are subtle differences between them. We can pick two, for instance. The first one is the difference in the memory address of pointers themselves and the memory address referred by array names. The other is initialization. We have seen that normal pointers have their own memory space allocated for them. However, a separate memory space is not allocated for array names, although they are considered as pointers. The memory address associated with array names is that of the first array element. In general, the memory returned by the address of operator on one-dimensional array names is the same the memory address of the first element. We have also seen that arrays can be initialized during or after declaration (except for character arrays) using braces. However, pointers cannot be initialized with braces even if they can be used as arrays. Having the above differences between arrays and pointers, let us now consider character arrays in accordance with the above concepts. Do you remember what we said about character arrays in chapter two? We said that they are given special consideration in C++. One of the ways in which they are treated differently is, the whole character array can be printed by printing the array name. With the concept in mind, let us now investigate what made character arrays so special in C++. For this, we will consider the following character array.
  • 10. By: Asaye Chemeda Email: asayechemeda@yahoo.com 35 To check that the array name program only refers to the address of the first element, we can see the truth values of the following logical statements. From the above, only the statement on Line 1 returns true. Hence, the name program only points to the first elements of the array. The following statements will print characters ‘C’, ‘+’ and ‘+’ respectively. Any of the elements of the character array can also be manipulated by using the dereferencing operator. So far, we are getting what we expected and everything seems normal. The character array name program is behaving just like any other array name. If so, what do you expect the output of the following statement will be? If the character array name program behaves like other array names, what is printed by the above statement would be the memory address of the first program array element. But, that is not what will be printed. All the array elements, and probably additional characters, will be printed. Even if we expect memory address of the character ‘C’ to be printed, all the characters in the array will printed as a string. Mysterious, isn’t it? The reason for the above mystery to happen will be solved by analyzing property of the ‘<<’ operator. The way how the ‘<<’ operator will respond when it is called into action is implemented in the iostream.h file. In the iostream.h, there are a number of overloaded functions to handle the responses when this operator is called in a program. Recalling our discussion on functions in chapter one, overloaded functions have the same name but different signatures. The overloaded function to handle the ‘<<’ operator is overloaded with several data types including pointers. Do you remember rule 1 of overloading which we discussed? The rule can be summarized as: when an overloaded function is called, the compiler tries to invoke the function with the best match formal parameter as the passed argument. Based on this rule, when pointers are passed to the function responding to calls made to ‘<<’ operator, there are two alternative overloaded functions with pointer formal parameters to be invoked. One of them has a data type for its formal parameter as const void* while the other one has const char*. When names of non-character arrays are passed as an argument, the overloaded function with the const void* as a formal parameter data type will be invoked. As a result, the memory address contained in the passed pointer, which is the array name, will be printed by the ‘<<’ operator. However, when the name of character arrays is passed, the overloaded function with const char* as a data type for the formal parameter will be invoked. This function doesn’t print the memory address contained in the name of the passed character array. Rather, all the characters in the array until the null character is encountered will be printed. If the character array is not null-terminated, additional characters may also be printed. Mystery solved! After all, character arrays behave in the same way as other arrays. The separate functions which handle exclusively character arrays, however, treat them differently and create the illusion that character arrays are different from others. We have seen how one-dimensional arrays can be used as pointers. Now, let us see the reverse- how pointers can be used as one-dimensional arrays. Again, we will consider a fragment of a code. Consider the following string array and string pointer. In the above code fragment, the name of the string array, str, which is actually a pointer to the first element of the array, “C++” is assigned to a string pointer, p. If p is dereferenced after Line 3, the return will be the string “C++”. If you answer what the following statement prints correctly, it means that you have fresh memory of arithmetic operations on pointers. In the above statement, the value of p pointer is pre- incremented by sizeof(string) and then dereferenced. The pre-increment will make p point to the second element of str array before being dereferenced. The dereferencing returns the value of the second element of the str array. Therefore, the above statement prints the string “is”. Is that how a pointer is used as an array? If so, where is the array subscripting operator ([]) used? These are good
  • 11. By: Asaye Chemeda Email: asayechemeda@yahoo.com 36 questions. Indeed, accessing array elements without pointers is characterized by the usage of indexing through the [] operator. The way the pointer p was used above to access the elements of str array doesn’t show that pointers can be used as arrays. Do you remember how name of array was used as a pointer to access array elements? The basic idea at that time was array name is a pointer and by dereferencing the array name with integer addition or subtraction, the array elements can be accessed and manipulated. Now, let us see how array elements are accessed by indexing using the array name. Since indexing starts from zero, str[0] refers to the value of the first element of str array, str[1] refers to the second and so on. By initializing the p pointer with array name str, we have synchronized them to refer to the memory address of the first element of str array. Now, let us try to use the pointer p as str the way that str could be used as p to access the array elements. If this is possible, i.e. if we can access and manipulate elements of str array by indexing p pointer without using the dereferencing operator, we can say that pointers can indeed be used as arrays. Of course, it is possible to access and manipulate elements of one-dimensional arrays by indexing pointers. For instance, p[0] refers to the value of the first element of str array. The other elements can also be accessed and manipulated in a similar manner. The following statement changes the third element of str array from “fun” to “awesome”. Note that, in the above statement, the array subscripting not the dereferencing operator was used on the pointer p to change the third str element. Note that using index of 2 with pointers may not necessarily refer to the third array element. It only means the third element from which the pointer is initialized with the address of. For instance, if a pointer is initialized with the address of the second element of the array, using index of 2 with the pointer refers the fourth element of the array not the third. Don’t you feel convinced that pointers can be used as arrays? Hopefully, you do. Now, think about details. Consider the following program and guess what will be printed by the cout statements. Note that p is initialized with the address of the second element of str array. If you answered both of them correctly, you have completely understood what we discussed so far about pointers and arrays. Program 3.4[Correct].cpp In the above program, p is initialized with the address of the second element of str array. Therefore, the indexing for p starts from the second element of str array. Based on this, p[0] refers the string “is” and p[1] refers to the third element of str array. Thus, the output of the cout statement on line 9 is the string “awesome”. The array name, however, always refers to the memory address of the first element. When 1 is added to an array name and dereferenced, the second element of the array is always returned. The output of the cout statement on line 10 is, therefore, the string “is”. One-dimensional arrays and pointers can be interchangeably used to access and manipulate array elements without much difference between them. Is this also the case for two-dimensional arrays? Can we apply what we discussed so far about pointers and arrays for two-dimensional arrays too? The answer for this is a mixed yes and no. before looking into how two- dimensional arrays can be used as pointers or vice-versa, let us see how elements of two-dimensional arrays are stored in the memory. When we consider two-dimensional arrays, what we normally visualize is simple data type variables arranged in two dimensions. If the array has (m+1) rows and (n+1) columns, we may expect it to be arranged as below in the memory. Figure 1: Two-dimensional array representation The question is: is this how two-dimensional arrays are actually stored in the memory? In two dimensions with rows and columns? Not really. Two-dimensional arrays are stored in the memory as one-dimensional arrays. The above two-dimensional array will be stored in the memory as:
  • 12. By: Asaye Chemeda Email: asayechemeda@yahoo.com 37 Figure 2: Equivalent one-dimensional array representation If two-dimensional arrays are stored in the memory as if they are one-dimensional arrays, don’t you think the basics of how they could be used interchangeably with pointers is already explained? We can say so but it may not be as straight forward as it was for one-dimensional arrays. The details are therefore important. Let us start discussing from what the name of a two- dimensional array represents. In one-dimensional array, the name of the array just represents a scalar pointer containing the memory address of the first element. Is that so for two-dimensional arrays too? Not really. The name of a two-dimensional array is a pointer to the first element of a one-dimensional array of pointers. The elements in this array of pointers are the memory addresses of the first elements in each row. When this is represented in diagram, we will have the following: Figure 3: Two-dimensional array direction The name of two dimensional array is, therefore, a pointer to a pointer. Even if the name of two-dimensional array is a pointer to a pointer, we can’t assign it to a scalar pointer to pointer. Are you getting lost? Or is it getting interesting? May be, if we use examples, it will be easier to understand and more exciting. Let us consider the following array declaration: In the above statement, marks array is declared to have three columns and is initialized with six elements. The array will have two rows but the compiler is not interested in the number of rows. Between the array name and the array elements, there is an array of pointers. The array name marks is a pointer to a pointer and doesn’t directly point to any of the array elements. If marks is dereferenced once, what is returned is a memory address not the first array element. To understand what marks points to, we can think of marks to be an array name for one-dimensional array of pointers with the following two elements. We can also consider the above two elements to be the array names for the first and second rows of the two- dimensional marks array respectively. They are memory addresses themselves but they can be considered as names of the two rows of marks array. If they are one- dimensional array names, they refer to the same memory address as the elements they point to without having memory addresses of their own. If the array name marks can be considered to have the above two elements, the identifier marks is supposed to take the memory address of &marks[0][0]. As an array name itself, &marks[0][0], refers to the memory address of the first element of the two- dimensional marks array. In other words, the array name marks also refers to the memory address of element marks[0][0]. Despite this, dereferencing marks only once, will not return the first element of the array. What a single dereferencing on marks returns is the address of marks[0][0]. However, if marks is dereferenced twice, the first element will be returned. Which means that the array name marks is a pointer to a pointer. The following logical statement will return true. Even if marks is a pointer to a pointer, it can’t be assigned to a scalar pointer to pointer. The following statement will cause compilation error.
  • 13. By: Asaye Chemeda Email: asayechemeda@yahoo.com 38 The reason for the compilation error is that pp is a scalar pointer to pointer while marks is a pointer to an array of pointers. The above assignment is just like trying to assign an array variable to a simple data type variable. If the reason behind the impossibility of above assignment is not clear, go through what we recently discussed about two-dimensional array names once again. Incorporating the above statements in a program with output statements and analyzing the results might also help for a better understanding. If two-dimensional array names are pointers to an array of pointers, then how are we going to use them to access and manipulate array elements as pointers? Good question at the right time. There are two options for this. One of them is using the array name partially as an array and partially as a pointer. The other option is by using the array name completely as a pointer. In the first option, the array name will be used to access the intermediate array of pointers by using the array subscripting operator. After this, the elements of the intermediate array of pointers will be used to access and manipulate the array elements through pointer arithmetic and dereferencing. In the second option, the data type of the array name will be casted from pointer to pointer to simply a pointer. Then, the casted array name will be used to access all the array elements through pointer arithmetic and dereferencing without using the array subscripting operators. Both of the above options will be discussed in detail one by one. Although the second option is easier, starting from the first option creates a smooth transition. We will again consider the marks two-dimensional array. Since the array has two rows, consider the array to be a set of two one-dimensional arrays each representing a single row. We have said that we can consider the array name marks to be an identifier for one-dimensional array of pointers. Each element in the one-dimensional array of pointers is a pointer to the first element of each row. Since the actual two-dimensional array has two rows, the one-dimensional array of pointers will have two elements. Now, we can write the marks array as follows: Note that, each element in the above one-dimensional array of pointers is an array name for each row of the actual two-dimensional marks array. If marks is an identifier for a one-dimensional array of two elements, how can we write the two elements in terms of marks? It is easy, right? We can write it using the array subscripting operator as follows: Since the two elements in the above marks array are names for each row, we can also write the following statements. You see how the name marks along with the [] operator is used as a name for each row. The names marks[0] and marks[1] will act as constant pointers. As long as we do not apply arithmetic operation which attempts to change their values, marks[0] and marks[1] can now be used as normal pointers. By dereferencing them, we can access and manipulate the all the array elements. For instance, the following program changes the value of marks[0][1] from 89.3 to 92.9 and prints all the elements of marks array. Program 3.5[Correct].cpp If you have understood that marks along with a single [] operator with the row number inside is just an array name for each row and if you recall how names of one dimensional arrays (such as each row of marks array) can be used as pointers, the above program is straight forward. The output of the above program is shown below. Program 3.5 shows how name of two-dimensional array can be used to access and manipulated all the elements of the array by the combination of subscripting and pointer dereferencing. Do you feel that you understood every piece of it? Or do you feel that have some doubts? Whichever your feeling is, you are ok. If you understood it, proceed to what we are going to discuss next. Otherwise, go through it once again, write your own programs, analyze outputs from different angles and you will definitely say “it isn’t that difficult”.
  • 14. By: Asaye Chemeda Email: asayechemeda@yahoo.com 39 Now, let us proceed to the second and easier way to access all elements in two-dimensional arrays by using the array name as a pointer. In this approach, consider the two-dimensional array to be a continuous one- dimensional array as shown in Fig. 2. The equivalent one- dimensional array is formed by appending each row at the end of the previous row. Can you imagine what should also be done during the conversion from two-dimensional to one-dimensional array? Two basic changes should be made. The first change should be the data type of the array name, not the individual array elements. The name of a two-dimensional arrays is a pointer to array of pointers while that of one-dimensional arrays is a pointer. Therefore, if the conversation is to be made, the data type of the array name should be casted accordingly. The casting can be done as follows: As long as we make the explicit casting as above consistently, the arrayName can be used as just a name of one dimensional array, i.e., the array name will behave as a pointer not a pointer to array of pointers. Consider the following two dimensional array declaration: Without casting, the array name sum is a pointer to a pointer. Remember, even if it is a pointer to a pointer, sum cannot be assigned to a scalar pointer to pointer. However, by explicitly casting it, we can make it behave just like an array name of a one dimensional array. We can imagine the above array to be as such: The following statement returns a value of 9, which is the value first element of sum array. Note that, in the above statement we used only one dereferencing operator. Even so, we accessed the value of the first element. Had we tried this without casting sum to (int *), we would have only got the address of the element. The value of the second element, which is 4, can be returned by the following statement. Therefore, by casting the names of two-dimensional arrays, we can use them as constant scalar pointers. Although the casting is essential, it is not the only change which should be applied to use two-dimensional array names as scalar pointers. In addition to casting, another change should also be made while converting two- dimensional arrays into an equivalent one-dimensional array. Can you guess what that change could be? It is the index of each element. When indexing through [] operator only is used to access elements of two-dimensional arrays, the row and column numbers should be explicitly stated. However, when the two-dimensional array is converted to an equivalent one-dimensional array, there will only be a single index-the row number. As a result, the row and column indexes which are supposed to access element in the two-dimensional array should be adjusted so that they can be used to access the same array element in the one- dimensional array. When two-dimensional arrays are condensed to form a one-dimensional array, the equivalent single index to be used for accessing the elements is determined from three values: row and column numbers of the element in the two-dimensional array and number of columns. That is why it is always mandatory to explicitly specify the column number during the definition of two dimensional arrays. Conversion Rule: If a two-dimensional array has n columns, for indexing puroses, it can be considered as a one-dimensional array in which the element on the ith row and jth column in the two dimensional array becomes kth element in the equivalent one-dimensional array, where k=i*n+j. If we apply casting and if we implement the above conversion rule properly to access the right element, using two dimensional array names as pointers becomes as easy as it is for one-dimensional array names. Do you agree with this or do you have a different view? Before deciding, let us have a look at the following program. Program 3.6[Correct].cpp In the above program, a two-dimensional array sum having three columns is declared and initialized on line 6. The single index for the equivalent one-dimensional array k is defined on line 7. Two for-loops were used to navigate through all the array elements. The outer for- loop with i counter is navigates across the row and the inner for-loop with j counter navigates across the column. If the array is to be converted to an equivalent
  • 15. By: Asaye Chemeda Email: asayechemeda@yahoo.com 40 one-dimensional array, the index to be used for this array (k) is calculated on line 10. The cout statement on line 11, prints two values. The first one prints the value of an array element through indexing only. The second one prints the same value through pointer only. When an element of the array is printed using pointer arithmetic, first the array name is casted. Then, the index in the equivalent one-dimensional array is added through pointer arithmetic to point to each array element. By dereferencing, the value of the array element is returned and printed. By the way, we don’t need two for-loops when accessing elements of two dimensional arrays through pointers. One for-loop which navigates through all the elements can also be used. Since the number of elements in the array is 9, the following single for-loop prints all the elements of sum array in the same way the above program did. What do you think the problem will be if we use the following piece of code instead of the above one? In other words, what could go wrong if we use pointer increment instead of integer addition as in the following piece of code? If you recall, the legal pointer arithmetic operations on array names, the answer is pretty simple. Array names are constant pointers and their values cannot be changed. Integer addition just adds an integer to the array name without changing the memory address pointed by the name. Integer addition through ++ operator, however, attempts to change value of the memory address contained in the array name, which is illegal. That is why the above piece of code is wrong. Clear enough? Aren’t you still convinced that using two-dimensional array names as pointers is not that difficult? What about after having the following formula? If a is the name of a two-dimensional array having n columns and a base data type of dataType, element a[i][j] can equivalently be accessed and manipulated using the array name as pointer using the following formula: So simple. Using two-dimensional array names as pointers has been discussed to the detail we need for this chapter. But, is that all what we are going to discuss about pointers? Not really. Pointers are one of the powerful blessings of C++ and mastering them should be a virtue of someone who wants to be a decent programmer. Few more interesting concepts are remaining and we will proceed to next one. Trust me. Our persistence will pay off. Before starting the topic of “Arrays and pointers”, we said that any curiosity that you might have about pointers to pointers will get response in this topic. The time has now come to answer what the applications of pointers to pointers are in C++ programming. One of the applications of pointers to pointers comes into picture, when we use pointers as two-dimensional arrays. Remember that we haven’t yet discussed how pointers can be used as two-dimensional arrays. Before proceeding to it, recalling the basics of two-dimensional array names might be advantageous. Two dimensional array names are pointers to an array of pointers. Because of this, the array name cannot be assigned to a scalar pointer to a pointer. The array of pointers stands between the name and array elements. As a result, the array elements cannot be directly accessed by only a single dereferencing of the array name. If we want to use pointers as two-dimensional arrays, there may be several options. One of them is by creating a pointer which is a replica of the property of the array name. There is also another option in which pointers to pointers can be used to create and used as dynamic arrays. Both the options will be covered in this chapter. In order to create a pointer with similar properties as a name of two-dimensional array, the pointer should be a pointer to an array of pointers. In addition, the elements in the array of pointers should point to the first element in each row of the two-dimensional array. If a pointer to pointer is declared in such a manner, it can be used as a two-dimensional array itself and the elements of the array can be accessed by subscripting. For a better understanding, let us consider the following two dimensional array. The above name array is a two-dimensional array with 11 columns and four rows. Each string literal is a row by itself. The characters in the above array can be arranged as follows.
  • 16. By: Asaye Chemeda Email: asayechemeda@yahoo.com 41 If we want to use a pointer to pointer which can be used as a two-dimensional array, first we need to create an array of pointers. The array of pointers will have the same number of elements as the number of rows in the name array. Let us declare the array of pointers as follows: Each element of the pointer array p should now be initialized with the memory address of the first element in each row of name array as follows: The scalar pointer to pointer, which can be used a two- dimensional array, can now be declared and initialized with the address of the first element of pointer array p. this is done as follows: The pointer pp is a pointer to an array of pointers. Isn’t pp a replica of the name array? Of course it is. Now, we can use subscripting on pp to access and manipulate any element of the name array. It just acts as a two dimensional array. The following for-loop prints all the characters as they are written during the declaration of name array. The scalar pointer to pointer pp was just used as an array. You see what the application of pointers to pointers could be. They can be used as arrays provided that they point to an array of pointers having elements, which in turn point to the first elements in each row of a two-dimensional array. The above procedure shows one of the ways in which pointers can be used as two dimensional arrays. Before proceeding to the last topic of this chapter, let us recall what we postponed to do in this topic. Can you guess what that might be? It is about passing two-dimensional arrays to functions. We put off its discussion, remember? To pass two dimensional arrays to functions, we can use different approaches. We can cast the array and pass it as a scalar pointer. We can define array of pointers with each element pointing to the element in the first column of the two-dimensional array. Then, we can pass the array pointers to a function which receives pointer arrays or scalar pointers to pointers. The following program shows three different approaches to pass two dimensional arrays. In the program, three functions will be defined and all of them print the last character of the name array given above. Program 3.7[Correct].cpp In the above program, the name array is passed to print1 function by casting it to a scalar character pointer on line 16. The formal parameter of print1 function is a scalar pointer and through pointer arithmetic each element of the name array can be accessed and manipulated. An array of pointers p is declared on line 17 and initialized on line 19. Then, this array of pointer is passed to print2 function on line 20. The formal parameter of print2 function is an array of pointers itself. The elements of name array can now be accessed and manipulated through a combination of subscripting and pointer arithmetic. The array of pointers p can also be passed to function which receives scalar pointers to pointers like the print3 function. Then, through subscripting, the elements of the name array can be accessed and manipulated. The output of the above program is displayed below. The output of all the three functions is the last character of the name array, which is ‘k’. Almost done. One important topic is remaining though. Before proceeding to the topic, let us refer to program 3.7. In the program, the name array is declared with 11 columns and initialized with four string literals (names).
  • 17. By: Asaye Chemeda Email: asayechemeda@yahoo.com 42 This means that any of the initializing string literals cannot have more than 10 characters (the remaining one column is for the null character). In other words, any of the initializing names cannot have more than 10 characters. Such arrays in which their size is defined before compile- time are known as static arrays. Now, assume you want to create a program which takes names of the customers of the company that you are working at. How many columns will you allocate for the array which will store the names? 11 or 20 or how many? This numbers of columns may be too small for some names. If having small number of columns is a problem, why not take a very large value for the column number so that there will not be a problem when any name is taken by the program? It seems a good solution. But, is it? If we declare the array with a large value for the array which is going to store names, don’t you think we are going to waste memory space when names with small number of characters are taken by the program? Then, why not leave mentioning the number of columns during declaration? That is not an option because the compiler won’t allow us. If using small number for the column may not store long names, if using large number of columns wastes memory space and if leaving the number of columns unmentioned is not an option, what could be the best solution? The solution for this is creating a dynamic array with dynamic array size. In dynamic arrays, any of the array sizes are so flexible that they can be determined after the program is compiled. The following topic will explain it more. Dynamic memory allocation Throughout this chapter, we have been discussing about pointers. Among the various concepts we discussed, we have seen how pointers store memory addresses of variables, how pointers are dereferenced and how pointers can be used as arrays. In all these cases, the pointers are useless unless there is a variable created beforehand. The interesting question is: if the variables can be manipulated without the use of pointers, why do we need to define additional data? Here is the answer. In C++ programming, memory space is allocated for all non-pointer variables during compile- time. However, there may be variables which only require memory space depending on the environment during run- time. Or variables which obtain their values during run- time might be necessary within a program for efficiency purposes. In such cases, pointers become powerful options for accessing a memory space allocated during run-time. The pointer variables which we had been dealing with in the previous topics obtain a memory space during compile-time. Therefore, all non-pointer variables and the type of pointer variables which we discussed so far obtain the memory space allocated for them during compile time. Such variables for which memory space is allocated for them during compile time are known as static variables. And those variables for which memory space is allocated for them during run-time are known as dynamic variables. The fact which makes pointers very useful is that dynamic variables cannot be created without pointers. Then, how do we define dynamic variables? Good question. In order to create dynamic variables in C++, a special word is used. Dynamic variables are created in C++ using the keyword new. If a variable is declared with new, its memory is allocated during run-time and the address of the allocated memory is returned. Therefore, the variable declared by the keyword new should always be a pointer. Note that dynamic memory space may not always be granted. Using new, two types of dynamic variables can be created: a pointer with single address and an array of pointers with multiple addresses. The syntax used to create dynamic variables is as follows. Dynamic single variables are declared as follows. In the above declaration, a memory space sufficient for a data type of dataType is allocated during run-time and the address is returned to pointer. For array of dynamic variables, In the above declaration, a memory space sufficient for Size number of elements each with a data type of dataType is allocated during run-time and the base address is returned to pointerArray. After pointers or pointer arrays are created dynamically, the elements which they point to can be accessed and manipulated through dereferencing. Once, a variable is defined using the keyword new, the memory space which is allocated for the variable should be de-allocated before using the same variable to obtain a new dynamic memory space. Otherwise, a situation known as memory leak will be created. This can be easily elaborated by the following code fragment. In the above code fragment, a double pointer p is declared on line 1. A dynamic memory is allocated for the dynamic variable which is going to be pointed to by p on
  • 18. By: Asaye Chemeda Email: asayechemeda@yahoo.com 43 line 2. The value of the dynamic variable is initialized with 30.28 on line 3. On line 4, the pointer p again obtains a dynamic memory space. On line 4, the value at the recent memory location pointed by p is initialized with a value of 38.73. Diagrammatically, this is depicted as follows. Let us say the addresses for the memory allocated on line 2 and on line 4 are 100 and 200 respectively. Let us focus on what happened on line 4. Even if p was pointing to memory address 100, a new dynamic memory, with address of 200, is allocated and the address is returned to p. Even if the value in memory address 100 may not be used after line 4, the memory address 100, can never be accessed. This situation is known as memory leak. The memory size of address 100 may be few bytes. However, thousands or millions of such leaks may result in running out of memory space and ultimate termination of the program. To avoid memory leaks, dynamic memory spaces can be de-allocated using the keyword delete. The way in which delete is used depends on the type of the dynamic memory to be de-allocated. To de-allocate memory spaces allocated for single variables, the following syntax is used. For de-allocation of memory spaces allocated for arrays of variables, the syntax is: We can improve the above code fragment as follows. The keyword delete only de-allocates the allocated dynamic memory for future use. It has nothing to do with the pointers or pointer arrays. After the dynamic memory is de-allocated using delete, the pointers may still point to the memory address they used to point. In such cases, the pointers are said to be dangling. To avoid the risk of dangling pointers, the pointers are usually assigned with NULL after the memory is de- allocated. After our discussion on how memory is allocated dynamically during program execution, let us see how dynamic arrays are created in C++. It may be useful recalling how pointers are used as arrays. Let us upgrade program 3.7 so that a user will be prompted to enter a name and let us store the entered characters in a two-dimensional dynamic array called pName. The values of the number of rows and columns will be obtained at run-time. As a result, both these values are variables. The number of rows will be denoted by rows and the number of columns by columns. The value of rows, which represents the number of names that is going to be stored in the dynamic array, will be entered by the customer. The value of columns, which represents the number of characters in the names entered by the customer, is determined from the length of the string which stores the entered names. To create two-dimensional dynamic arrays, we need to define pointers to pointers. Let us denote the pointer to pointer which will be used as a dynamic two-dimensional array a name of pName. Note that both pName and *pName are pointers. To create a two-dimensional array, pName will be an array of pointers. Each element in the array of pointers will be an array name to an array of characters. The array of characters will be the characters in the names entered by the customer. First, pName will be defined to be an array of pointers with dynamically allocated memory space. Each element of the pName array can be accessed and initialized through subscripting. Note that the value of rows in the above declaration will be determined during program execution. Therefore, the number of names that the dynamic array is going to store will be determined at run time? Isn’t this an elegant way of optimizing your memory space? Of course it is. More is yet to come. The number of columns will also be made to be dynamic. If i represents a row number, memory is allocated dynamically for each column in the row under consideration by using the following statement.
  • 19. By: Asaye Chemeda Email: asayechemeda@yahoo.com 44 After this definition, pName will become a dynamic two- dimensional array and its elements can be accessed and manipulated through indexing. During our discussion in chapter two, we said that functions cannot return arrays. However, this will not be a problem anymore. Can you guess why? Because we have pointers. Even though functions cannot return arrays, they can return pointers. Obviously, pointers can act as arrays. The logic is simple now. If functions can return pointers and if pointers can act as arrays, why don’t we return arrays as pointers? You see another importance of pointers. In general, if a function has to return an array, a pointer can substitute the array and the pointer can be returned as an array. Let us incorporate what we discussed about dynamic arrays and returning arrays as pointers in a program which improves the drawbacks that could happen if we use static arrays. Here is the program. Program 3.8[Correct].cpp In the above program, there are three functions including main function. The values of the variables rows and columns are obtained at run-time but the variables are made global so that every function can access them. In the main function, a pointer to pointer pName is defined on line 8. The pointer will be used to create the dynamic array. On line 9, the customer is prompted to enter the number of names that he wants to enter. This is just to show the dynamic nature of the array which we are going to create by entering different numbers of names for the dynamic array to hold. The number which will be entered by the user will be the number of rows in the array. Therefore, it will be stored in the rows variable. The purpose of the statement in line 11 will be explained in the next chapter. On line 12, a dynamic memory is allocated for array of char pointers and the base address is returned to pName. Each element in the array of pointers will be used point to the base address of the characters in a single name. Therefore, the number of elements in this array of pointers will be equal to rows and the assignment to pName was made as such. On line 13, a function named print is called by passing pName as an argument. The formal parameter of the print function is also made to conform to the data type of the passed argument. Within print function, a pointer to pointer is defined and it is initialized by the pointer returned by read function. The read function takes a pointer to a pointer of char data type as a parameter. It is the read function which reads the names entered by the customer and stores them in the dynamic array accordingly. Note that, since the read function returns a pointer to pointer, the return type of the function is stated as such. In the read function, a string variable sName is declared on line 18. This variable takes the name of one customer. There are two-four loops within the function. The outer for-loop loops across the rows while the inner for-loop loops across all the characters in a given name. Within the outer for-loop, the customer is prompted to enter his name on line 20. The statement on line 21, which will be explained in detail in the next chapter, reads all the characters in a string entered by the customer until the ‘Enter’ key is pressed. The entered string will be stored in the sName variable. Here comes another dynamic nature of the array which we are going to create. The number of columns in a given row will be determined after determining the number of characters in the entered name. Isn’t this what we wanted to improve in program 3.7? The number of characters in
  • 20. By: Asaye Chemeda Email: asayechemeda@yahoo.com 45 the entered name is determined by determining the length of sName as it is done on line 22. The determined number of characters plus one space for the null character is then assigned to the columns variable. This means that the value of the columns variable is determined at run-time. On line 23, a dynamic memory is allocated for an array having number of elements equal to columns. This dynamic array will be the dynamic column for each row of the two-dimensional dynamic array. You see, entering long names is not a problem anymore and no memory space is going to be wasted. The exact memory requirement will be allocated for any name entered. On the same line of the program, i.e. line 23, the array of pointers in name variable, which also refers to the same memory address as pName, is assigned with the base address of the dynamic memory allocated for each column with the help of the array subscripting operator []. For instance, pName[i] contains the base memory address of the array of characters which stores the ith name entered by the customer. After this, each column in the given row is assigned with the corresponding character in the sName string on line 25. The sName string changes every time the customer enters a name and presses the ‘Enter’ key. On line 26, the last character in each row is assigned with null character. Remember that on line 22, one is added to the length of sName in the assignment to columns to accommodate the null character. After all names are entered and stored in the dynamic array, the pointer to pointer name is returned by the read function. This returned pointer to pointer is then used to assign another pointer to pointer defined in the print function. Through this pointer to pointer, all the names stored in the dynamic array can be accessed by indexing. A four loop and a while-loop inside the print function will print every character in the entered names until a null character is encountered. How dynamic was that? There could also be other easier ways by which a program with the same goal as program 3.8 can be written. However, the program was also intended to show the implementation of our recent discussion on dynamic arrays and returning arrays as pointers. Indeed, the program created a dynamic array which improves the efficiency of memory allocation. That is all for this chapter. It was relatively longer than the other chapters. But, wasn’t it worth? Since pointers are areas in C++ which can lead even a professional programmer to write faulty programs which are difficult to bug, the time and the effort we spent in this chapter will be rewarded. Besides, pointers provide us the means by which we can manipulate data in a program which other approaches may not be capable of. So, the devotion we put in this chapter is justified. Even if this and the previous chapters covered lots of basic components in C++ programming, wring an efficient program may ask a lot more. As we have progressed from chapter to chapter, we have gathered more and more knowledge that we need to have as a programmer. So, do you want to know more? I know your answer is also yes because ‘C++ is fun’. Isn’t it? Let us go to the next chapter then.