0% found this document useful (0 votes)
5 views26 pages

Chapter 2 - Arrays

Chapter Two discusses arrays and strings, introducing arrays as a data structure that allows storing multiple values of the same type using a collective name and unique indices. It explains the declaration, initialization, and accessing of one-dimensional arrays, along with examples of using arrays in programs to handle data efficiently. The chapter emphasizes the importance of managing array bounds to avoid errors such as subscript overflow.

Uploaded by

negedetekleyes33
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
5 views26 pages

Chapter 2 - Arrays

Chapter Two discusses arrays and strings, introducing arrays as a data structure that allows storing multiple values of the same type using a collective name and unique indices. It explains the declaration, initialization, and accessing of one-dimensional arrays, along with examples of using arrays in programs to handle data efficiently. The chapter emphasizes the importance of managing array bounds to avoid errors such as subscript overflow.

Uploaded by

negedetekleyes33
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Chapter Two

2. Arrays and Strings

2.1. Introduction
Variables in a program have values associated with them. During program execution
these values are accessed by using the identifier associated with the variable in
expressions etc. In none of the programs written so far have very many variables been
used to represent the values that were required. Thus even though programs have been
written that could handle large lists of numbers it has not been necessary to use a separate
identifier for each number in the list. This is because in all these programs it has never
been necessary to keep a note of each number individually for later processing. For
example in summing the numbers in a list only one variable was used to hold the current
entered number which was added to the accumulated sum and was then overwritten by
the next number entered. If that value were required again later in the program there
would be no way of accessing it because the value has now been overwritten by the later
input.

If only a few values were involved a different identifier could be declared for each
variable, but now a loop could not be used to enter the values. Using a loop and assuming
that after a value has been entered and used no further use will be made of it allows the
following code to be written. This code enters six numbers and outputs their sum:

sum = 0.0;
for (i = 0; i < 6; i++)
{
cin >> x;
sum += x;
}
This of course is easily extended to n values where n can be as large as required.
However if it was required to access the values later the above would not be suitable. It
would be possible to do it as follows by setting up six individual variables:

float a, b, c, d, e, f;

and then handling each value individually as follows:


sum = 0.0;
cin >> a; sum += a;
cin >> b; sum += b;
cin >> c; sum += c;
cin >> d; sum += d;
cin >> e; sum += e;
cin >> f; sum += f;

which is obviously a very tedious way to program. To extend this solution so that it
would work with more than six values then more declarations would have to be added,
extra assignment statements added and the program re-compiled. If there were 10000
values imagine the tedium of typing the program (and making up variable names and
remembering which is which)!

To get round this difficulty all high-level programming languages use the concept of a
data structure called an Array.

2.2. What is an arrays


An array is a data structure which allows a collective name to be given to a group of
elements which all have the same type. An individual element of an array is identified by
its own unique index (or subscript).

An array can be thought of as a collection of numbered boxes each containing one data
item. The number associated with the box is the index of the item. To access a particular
item the index of the box associated with the item is used to access the appropriate box.
The index must be an integer and indicates the position of the element in the array. Thus
the elements of an array are ordered by the index.

2.3. One Dimensional Array


2.3.1. Declaration of Arrays
An array declaration is very similar to a variable declaration. First a type is given for the
elements of the array, then an identifier for the array and, within square brackets, the
number of elements in the array. The number of elements must be an integer.

For example data on the average temperature over the year in Ethiopia for each of the last
100 years could be stored in an array declared as follows:
float annual_temp[100];
This declaration will cause the compiler to allocate space for 100 consecutive float
variables in memory. The number of elements in an array must be fixed at compile time.
It is best to make the array size a constant and then, if required, the program can be
changed to handle a different size of array by changing the value of the constant,

const int NE = 100;


float annual_temp[NE];

then if more records come to light it is easy to amend the program to cope with more
values by changing the value of NE. This works because the compiler knows the value of
the constant NE at compile time and can allocate an appropriate amount of space for the
array. It would not work if an ordinary variable was used for the size in the array
declaration since at compile time the compiler would not know a value for it.

2.3.2. Accessing Array Elements


Given the declaration above of a 100-element array the compiler reserves space for 100
consecutive floating point values and accesses these values using an index/subscript that
takes values from 0 to 99. The first element in an array in C++ always has the index 0,
and if the array has n elements the last element will have the index n-1.

An array element is accessed by writing the identifier of the array followed by the
subscript in square brackets. Thus to set the 15th element of the array above to 1.5 the
following assignment is used:

annual_temp[14] = 1.5;

Note that since the first element is at index 0, then the ith element is at index i-1. Hence
in the above the 15th element has index 14.

An array element can be used anywhere an identifier may be used. Here are some
examples assuming the following declarations:

const int NE = 100,


N = 50;
int i, j, count[N];
float annual_temp[NE];
float sum, av1, av2;

A value can be read into an array element directly, using cin

cin >> count[i];


The element can be increased by 5,
count[i] = count[i] + 5;

or, using the shorthand form of the assignment

count[i] += 5;
Array elements can form part of the condition for an if statement, or indeed, for any other
logical expression:

if (annual_temp[j] < 10.0)


cout << "It was cold this year "
<< endl;
for statements are the usual means of accessing every element in an array. Here, the first
NE elements of the array annual_temp are given values from the input stream cin.

for (i = 0; i < NE; i++)


cin >> annual_temp[i];

The following code finds the average temperature recorded in the first ten elements of the
array.

sum = 0.0;
for (i = 0; i <10; i++)
sum += annual_temp[i];
av1 = sum / 10;

Notice that it is good practice to use named constants, rather than literal numbers such as
10. If the program is changed to take the average of the first 20 entries, then it all too easy
to forget to change a 10 to 20. If a const is used consistently, then changing its value will
be all that is necessary.

For example, the following example finds the average of the last k entries in the array. k
could either be a variable, or a declared constant. Observe that a change in the value of k
will still calculate the correct average (provided k<=NE).

sum = 0.0;
for (i = NE - k; i < NE; i++)
sum += annual_temp[i];
av2 = sum / k;

Important - C++ does not check that the subscript that is used to reference an array
element actually lies in the subscript range of the array. Thus C++ will allow the
assignment of a value to annual_temp[200], however the effect of this assignment is
unpredictable. For example it could lead to the program attempting to assign a value to a
memory element that is outside the program's allocated memory space. This would lead
to the program being terminated by the operating system. Alternatively it might actually
access a memory location that is within the allocated memory space of the program and
assign a value to that location, changing the value of the variable in your program which
is actually associated with that memory location, or overwriting the machine code of your
program. Similarly reading a value from annual_temp[200] might access a value that
has not been set by the program or might be the value of another variable. It is the
programmer's responsibility to ensure that if an array is declared with n elements then no
attempt is made to reference any element with a subscript outside the range 0 to n-1.
Using an index, or subscript, that is out of range is called Subscript Overflow. Subscript
overflow is one of the commonest causes of erroneous results and can frequently cause
very strange and hard to spot errors in programs.

2.3.3. Initialization of arrays


The initialization of simple variables in their declaration has already been covered. An
array can be initialized in a similar manner. In this case the initial values are given as a
list enclosed in curly brackets. For example initializing an array to hold the first few
prime numbers could be written as follows:

int primes[] = {1, 2, 3, 5, 7, 11, 13};

Note that the array has not been given a size, the compiler will make it large enough to
hold the number of elements in the list. In this case primes would be allocated space for
seven elements. If the array is given a size then this size must be greater than or equal to
the number of elements in the initialization list. For example:

int primes[10] = {1, 2, 3, 5, 7};


would reserve space for a ten element array but would only initialize the first five
elements.

Example Program: Printing Outliers in Data

The requirement specification for a program is:

A set of positive data values (200) are available. It is required to find the average value of
these values and to count the number of values that are more than 10% above the average
value.
Since the data values are all positive a negative value can be used as a sentinel to signal
the end of data entry. Obviously this is a problem in which an array must be used since
the values must first be entered to find the average and then each value must be compared
with this average. Hence the use of an array to store the entered values for later re-use.

An initial algorithmic description is:


initialize.
enter elements into array and sum elements.
evaluate average.
scan array and count number greater than
10% above average.
output results.
This can be expanded to the complete algorithmic description:
set sum to zero.
set count to zero.
set nogt10 to zero.
enter first value.
while value is positive
{
put value in array element with index count.
add value to sum.
increment count.
enter a value.
}
average = sum/count.
for index taking values 0 to count-1
if array[index] greater than 1.1*average
then increment nogt10.
output average, count and nogt10.

In the above the variable nogt10 is the number greater than 10% above the average value.
It is easy to argue that after exiting the while loop, count is set to the number of positive
numbers entered. Before entering the loop count is set to zero and the first number is
entered, that is count is one less than the number of numbers entered. Each time round the
loop another number is entered and count is incremented hence count remains one less
than the number of numbers entered. But the number of numbers entered is one greater
than the number of positive numbers so count is therefore equal to the number of positive
numbers.

A main() program written from the above algorithmic description is given below:

void main()
{
const int NE = 200; // maximum no of elements in array
float sum = 0.0; // accumulates sum
int count = 0; // number of elements entered
int nogt10 = 0; // counts no greater than 10%
// above average
float x; // holds each no as input
float indata[NE]; // array to hold input
float average; // average value of input values
int i; // control variable

// Data entry, accumulate sum and count


// number of +ve numbers entered
cout << "Enter numbers, -ve no to terminate: " << endl;
cin >> x;
while (x >= 0.0)
{
sum = sum + x;
indata[count] = x;
count = count + 1;
cin >> x;
}

// calculate average
average = sum/count;

// Now compare input elements with average


for (i = 0; i < count; i++)
{
if (indata[i] > 1.1 * average)
nogt10++;
}

// Output results
cout << "Number of values input is " << n;
cout << endl
<< "Number more than 10% above average is "
<< nogt10 << endl;
}

Since it was assumed in the specification that there would be less than 200 values the
array size is set at 200. In running the program less than 200 elements may be entered, if
n elements where n < 200 elements are entered then they will occupy the first n places in
the array indata. It is common to set an array size to a value that is the maximum we
think will occur in practice, though often not all this space will be used.

Example Program: Test of Random Numbers

The following program simulates the throwing of a dice by using a random number
generator to generate integers in the range 0 to 5. The user is asked to enter the number of
trials and the program outputs how many times each possible number occurred.

An array has been used to hold the six counts. This allows the program to increment the
correct count using one statement inside the loop rather than using a switch statement
with six cases to choose between variables if separate variables had been used for each
count. Also it is easy to change the number of sides on the dice by changing a constant.
Because C++ arrays start at subscript 0 the count for an i occurring on a throw is held in
the i-1th element of this count array. By changing the value of the constant die_sides the
program could be used to simulate a die_sides-sided die without any further change.

#include <iostream.h>
#include <stdlib.h> // time.h and stdlib.h required for
#include <time.h> // random number generation

void main()
{
const int die_sides = 6; // maxr-sided die
int count[die_sides]; // holds count of each
// possible value
int no_trials, // number of trials
roll, // random integer
i; // control variable
float sample; // random fraction 0 .. 1

// initialize random number generation and count


// array and input no of trials
srand(time(0));
for (i=0; i < die_sides; i++)
count[i] = 0;
cout << "How many trials? ";
cin >> no_trials;

// carry out trials


for (i = 0; i < no_trials; i++)
{
sample = rand()/float(RAND_MAX);
roll = int ( die_sides * sample);
// returns a random integer in 0 to die_sides-1
count[roll]++; // increment count
}

// Now output results


for (i = 0; i < die_sides; i++)
{
cout << endl << "Number of occurrences of "
<< (i+1) << " was " << count[i];
}
cout << endl;
}

2.3.4. Copying Arrays


The assignment operator cannot be applied to array variables:

const int SIZE=10


int x [SIZE] ;
int y [SIZE] ;
x = y ; // Error - Illegal
Only individual elements can be assigned to using the index operator, e.g., x[1] =
y[2];.
To make all elements in 'x' the same as those in 'y' (equivalent to assignment), a loop has
to be used.
// Loop to do copying, one element at a time
for (int i = 0 ; i < SIZE; i++)
x[i] = y[i];
This code will copy the elements of array y into x, overwriting the original contents of x.
A loop like this has to be written whenever an array assignment is needed.

Notice the use of a constant to store the array size. This avoids the literal constant '10'
appearing a number times in the code. If the code needs to be edited to use different sized
arrays, only the constant needs to be changed. If the constant is not used, all the '10's
would have to be changed individually - it is easy to miss one out.

2.4. Multidimensional arrays


An array may have more than one dimension. Each dimension is represented as a
subscript in the array. Therefore a two dimensional array has two subscripts, a three
dimensional array has three subscripts, and so on.

Arrays can have any number of dimensions, although most of the arrays that you create
will likely be of one or two dimensions.

A chess board is a good example of a two-dimensional array. One dimension represents


the eight rows, the other dimension represents the eight columns.

Suppose the program contains a class named square. The declaration of array named
board that represents would be

Square board[8][8];

The program could also represent the same data with a one dimensional, 64-square array.
For example, it could include the statement

Square board[64];
Such a representation does not correspond as closely to the real-world object as the two
dimensional array, however.
Suppose that when the game begins. The king id located in the fourth position in the first
row. Counting from zero that position corresponds to board[0][3] in the two dimensional
array, assuming that the first subscript corresponds to the row, and the second to the
column.

2.4.1. Initializing Multidimensional Arrays


To initialize a multidimensional arrays , you must assign the list of values to array
elements in order, with last array subscript changing while the first subscript while the
first subscript holds steady. Therefore, if the program has an array int
theArray[5][3], the first three elements go int theArray[0]; the next three
into theArray[1]; and so forth.

The program initializes this array by writing


int theArray[5][3] ={ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15};
for the sake of clarity, the program could group the initializations with braces, as shown
below.
int theArray[5][3] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9},
{10, 11, 12}, {13, 14,15} };
The compiler ignores the inner braces, which clarify how the numbers are distributed.
Each value should be separated by comma, regardless of whither inner braces are include.
The entire initialization must set must appear within braces, and it must end with a
semicolon.

2.4.2. Omitting the Array Size


If a one-dimensional array is initialized, the size can be omitted as it can be found from
the number of initializing elements:

int x[] = { 1, 2, 3, 4} ;
This initialization creates an array of four elements.
Note however:
int x[][] = { {1,2}, {3,4} } ; // error is not allowed.
and must be written
int x[2][2] = { {1,2}, {3,4} } ;
Example of multidimensional array
#include<iostream.h>
void main(){
int SomeArray[5][2] = {{0,0},{1,2}, {2,4},{3,6},
{4,8}}
for ( int i=0; i<5; i++)
for (int j = 0; j<2;j++)
{
cout<<"SomeArray["<<i<<"]["<<j<<'']: '';
cout<<endl<<SomeArray[i][ j];
}
}

2.5. Strings representation and manipulation


String in C++ is nothing but a sequence of character in which the last character is the null
character ‘\0’. The null character indicates the end of the string. Any array of character
can be converted into string type in C++ by appending this special character at the end of
the array sequence.

In C++ strings of characters are held as an array of characters, one character held in
each array element. In addition a special null character, represented by `\0', is appended
to the end of the string to indicate the end of the string. Hence if a string has n characters
then it requires an n+1 element array (at least) to store it. Thus the character `a' is stored
in a single byte, whereas the single-character string "a" is stored in two consecutive bytes
holding the character `a' and the null character.

A string variable s1 could be declared as follows:


char s1[10];

The string variable s1 could hold strings of length up to nine characters since space is
needed for the final null character. Strings can be initialized at the time of declaration just
as other variables are initialized. For example:

char s1[] = "example";


char s2[20] = "another example"
would store the two strings as follows:
s1 |e|x|a|m|p|l|e|\0|
s2 |a|n|o|t|h|e|r| |e|x|a|m|p|l|e|\0|?|?|?|?|

In the first case the array would be allocated space for eight characters, that is space for
the seven characters of the string and the null character. In the second case the string is
set by the declaration to be twenty characters long but only sixteen of these characters are
set, i.e. the fifteen characters of the string and the null character. Note that the length of a
string does not include the terminating null character.

2.5.1. String Output

A string is output by sending it to an output stream, for example:

cout << "The string s1 is " << s1 << endl;


would print
The string s1 is example
The setw(width) I/O manipulator can be used before outputting a string, the string will
then be output right-justified in the field width. If the field width is less than the length of
the string then the field width will be expanded to fit the string exactly. If the string is to
be left-justified in the field then the setiosflags manipulator with the argument
ios::left can be used.

2.5.2. String Input


When the input stream cin is used space characters, newline etc. are used as separators
and terminators. Thus when inputting numeric data cin skips over any leading spaces and
terminates reading a value when it finds a white-space character (space, tab, newline
etc. ). This same system is used for the input of strings, hence a string to be input cannot
start with leading spaces, also if it has a space character in the middle then input will be
terminated on that space character. The null character will be appended to the end of the
string in the character array by the stream functions. If the string s1 was initialized as in
the previous section, then the statement

cin << s1;


would set the string s1 as follows when the string "first" is entered (without the double
quotes)
|f|i|r|s|t|\0|e|\0|

Note that the last two elements are a relic of the initialization at declaration time. If the
string that is entered is longer than the space available for it in the character array then
C++ will just write over whatever space comes next in memory. This can cause some
very strange errors when some of your other variables reside in that space!

To read text containing blanks we use another function, cin::get().


#include<iostream.h>
void main()
{
const int max=80;
char str[max];
cout<<"\n Enter a string;";
[Link](str,max); // max avoid buffer overflow
cout<<"\n You entered : "<<str;
}
Reading multiple lines

We have solved the problem of reading strings with embedded blanks, but what about
strings with multiple lines? It turns out that the cin::get() function can take a third
argument to help out in this situation.

This argument specifies the character that tells the function to stop reading. The default
value of this argument is the newline('\n')character, but if you call the function
with some other character for this argument, the default will be overridden by the
specified character.

In the next example, we call the function with a dollar sign ('$') as the third argument
//reads multiple lines, terminates on '$' character
#include<iostream.h>
void main(){
const int max=80;
char str[max];
cout<<"\n Enter a string:\n";
[Link](str, max, '$'); //terminates with $
cout<<\n You entered:\n"<<str; }

now you can type as many lines of input as you want. The function will continue to
accept characters until you enter the terminated character $ (or untill you exceed the size
of the array. Remember, you must still press Enter key after typing the '$' character .

2.5.3. Avoiding buffer over flow


The strings in the program invites the user to type in a string. What happens if the user
enters a string that is longer than the array used to hold it? There is no built-in mechanism
in C++ to keep a program from inserting array elements outside an array.

However, it is possible to tell the >> operator to limit the number of characters it places
in an array.

//avoids buffer overflow with [Link]


#include<iostream.h>
#include<iomanip.h> //for setw
void main(){
const int MAX=20;
char str[MAX];
cout<<"\n Enter a string: ";
cin>>setw(MAX)>>str;
cout<<"\n You entered :"<<str;
}

2.5.4. String constants


You can initialize a string to a constant value when you define it. Here's an example'

#include<iostream.h>
void main(){
char str[] = "Welcome to C++ programming language";
cout<<str;
}

if you tried to the string program with strings that contain more than one word , you may
have unpleasant surprise. Copying string the hard way

The best way to understand the true nature of strings is to deal with them character by
character

#include<iostream.h>
#include<string.h> //for strlen()
void main()
{
const int max=80;
char str1[]='' Oh, Captain, my Captain!"
our fearful trip is done";
char str2[max];
for(int i=0; i<strlen(str1);i++)
str2[i]=str1[1];
str2[i]='\0';
cout<<endl;
cout<<str2;
}

2.5.5. Copying string the easy way


Ofcourse you don't need to use a for loop to copy a string. As you might have guesses, a
library function will do it for you. You can copy strings using strcpy or strncpy
function. We assign strings by using the string copy function strcpy. The prototype for
this function is in string.h.

strcpy(destination, source);
strcpy copies characters from the location specified by source to the location
specified by destination. It stops copying characters after it copies the terminating null
character.

o The return value is the value of the destination parameter.


You must make sure that the destination string is large enough to hold all of the
characters in the source string (including the terminating null character).

Example:
#include <iostream.h>
#include <string.h>
void main(){
char me[20] = "David";
cout << me << endl;
strcpy(me, "YouAreNotMe");
cout << me << endl ;
return;
}
There is also another function strncpy, is like strcpy, except that it copies only a
specified number of characters.
strncpy(destination, source, int n);

It may not copy the terminating null character.


Example
#include <iostream.h>
#include <string.h>
void main() {
char str1[] = "String test";
char str2[] = "Hello";
char one[10];
strncpy(one, str1, 9);
one[9] = '\0';
cout << one << endl;
strncpy(one, str2, 2);
cout << one << endl;
strcpy(one, str2);
cout << one << endl;
}

2.5.6. Concatenating strings


In C++ the + operator cannot normally be used to concatenate string, as it can in some
languages such as BASIC; that is you can't say

Str3 = str1 + str2;

You can use strcat() or strncat


The function strcat concatenates (appends) one string to the end of another string.

strcat(destination, source);
o The first character of the source string is copied to the location of the terminating null
character of the destination string.
o The destination string must have enough space to hold both strings and a terminating
null character.
Example:
#include <iostream.h>
#include <string.h>

void main() {
char str1[30];
strcpy(str1, "abc");
cout << str1 << endl;
strcat(str1, "def");
cout << str1 << endl;

char str2[] = "xyz";


strcat(str1, str2);
cout << str1 << endl;
str1[4] = '\0';
cout << str1 << endl;
}

The function strncat is like strcat except that it copies only a specified number of
characters.
strncat(destination, source, int n);
It may not copy the terminating null character.
Example:
#include <iostream.h>
#include <string.h>
void main() {
char str1[30];
strcpy(str1, "abc");
cout << str1 << endl;
strncat(str1, "def", 2);
str1[5] = '\0';
cout << str1 << endl;
char str2[] = "xyz";
strcat(str1, str2);
cout << str1 << endl;
str1[4] = '\0';
cout << str1 << endl;
}

2.5.7. Comparing strings


Strings can be compared using strcmp or strncmp functions
The function strcmp compares two strings.
strcmp(str1, str2);
strcmp returns: <0 if str1 is less than str2
=0 if str1 is equal to str2
>0 if str1 is greater than str2
Example:
#include <iostream.h>
#include <string.h>
void main() {
cout << strcmp("abc", "def") << endl;
cout << strcmp("def", "abc") << endl;
cout << strcmp("abc", "abc") << endl;
cout << strcmp("abc", "abcdef") << endl;
cout << strcmp("abc", "ABC") << endl;
}
The function strncmp is like strcmp except that it compares only a specified number
of characters.
strncmp(str1, str2, int n);

strncmp does not compare characters after a terminating null character has been found
in one of the strings.
Example:
#include <iostream.h>
#include <string.h>
void main()
{
cout << strncmp("abc", "def", 2) << endl;
cout << strncmp("abc", "abcdef", 3) << endl;
cout << strncmp("abc", "abcdef", 2) << endl;
cout << strncmp("abc", "abcdef", 5) << endl;
cout << strncmp("abc", "abcdef", 20) << endl;
}

2.6. Pointer
A pointer is the memory address of a variable. However, even though a pointer is a
memory address and a memory address is a number, you cannot store a pointer in a
variable of type int or double . A variable to hold a pointer must be declared to have a
pointer type. For example, the following declares p to be a pointer variable that can hold
one pointer that points to a variable of type double : double *p; The variable p can hold
pointers to variables of type double , but it cannot normally contain a pointer to a variable
of some other type, such as int or char . Each variable type requires a different pointer
type. For example, the following declares the variables p1 and p2 so they can hold
pointers to variables of type int ; it also declares two ordinary variables v1 and v2 of type
int: int *p1, *p2, v1, v2; There must be an asterisk before each of the pointer variables. If
you omit the second asterisk in the above declaration, then p2 will not be a pointer
variable; it will instead be an ordinary variable of type int .

2.6.1. Reference operator (&) and Dereference operator (*)

Reference operator (&):As soon as we declare a variable, the amount of memory


needed is assigned for it at a specific location in memory (its memory address). We
generally do not actively decide the exact location of the variable within the panel of cells
that we have imagined the memory to be - Fortunately, that is a task automatically
performed by the operating system during runtime. However, in some cases we may be
interested in knowing the address where our variable is being stored during runtime in
order to operate with relative positions to it.

The address that locates a variable within memory is what we call a reference to that
variable. This reference to a variable can be obtained by preceding the identifier of a
variable with an ampersand sign (&), known as reference operator, and which can be
literally translated as "address of". For example:

ted = &andy;

This would assign to ted the address of variable andy, since when preceding the name of
the variable andy with the reference operator (&) we are no longer talking about the
content of the variable itself, but about its reference (i.e., its address in memory).

From now on we are going to assume that andy is placed during runtime in the memory
address 1776. This number (1776) is just an arbitrary assumption we are inventing right
now in order to help clarify some concepts in this tutorial, but in reality, we cannot know
before runtime the real value the address of a variable will have in memory.
Consider the following code fragment:

1 andy = 25;
2 fred = andy;
3 ted = &andy;

The values contained in each variable after the execution of this, are shown in the
following diagram:
First, we have assigned the value 25 to andy (a variable whose address in memory we
have assumed to be 1776).

The second statement copied to fred the content of variable andy (which is 25). This is a
standard assignment operation, as we have done so many times before.

Finally, the third statement copies to ted not the value contained in andy but a reference
to it (i.e., its address, which we have assumed to be 1776). The reason is that in this third
assignment operation we have preceded the identifier andy with the reference operator
(&), so we were no longer referring to the value of andy but to its reference (its address
in memory).
The variable that stores the reference to another variable (like ted in the previous
example) is what we call a pointer. Pointers are a very powerful feature of the C++
language that has many uses in advanced programming. Farther ahead, we will see how
this type of variable is used and declared.

Dereference operator (*):


We have just seen that a variable which stores a reference to another variable is called a
pointer. Pointers are said to "point to" the variable whose reference they store.

Using a pointer we can directly access the value stored in the variable which it points to.
To do this, we simply have to precede the pointer's identifier with an asterisk (*), which
acts as dereference operator and that can be literally translated to "value pointed by".
Therefore, following with the values of the previous example, if we write:

beth = *ted;

(that we could read as: "beth equal to value pointed by ted") beth would take the value
25, since ted is 1776, and the value pointed by 1776 is 25.

You must clearly differentiate that the expression ted refers to the value 1776, while *ted
(with an asterisk * preceding the identifier) refers to the value stored at address 1776,
which in this case is 25. Notice the difference of including or not including the
dereference operator (I have included an explanatory commentary of how each of these
two expressions could be read):

1 beth = ted; // beth equal to ted ( 1776 )


2 beth = *ted; // beth equal to value pointed by ted ( 25 )

Notice the difference between the reference and dereference operators:

 & is the reference operator and can be read as "address


of"
 * is the dereference operator and can be read as "value
pointed by"

Thus, they have complementary (or opposite) meanings. A variable referenced with &
can be dereferenced with *.
Earlier we performed the following two assignment operations:

1 andy = 25;
2 ted = &andy;

Right after these two statements, all of the following expressions would give true as
result:

1 andy == 25
2 &andy == 1776
3 ted == 1776
4 *ted == 25
The first expression is quite clear considering that the assignment operation performed on
andy was andy=25. The second one uses the reference operator (&), which returns the
address of variable andy, which we assumed it to have a value of 1776. The third one is
somewhat obvious since the second expression was true and the assignment operation
performed on ted was ted=&andy. The fourth expression uses the dereference operator
(*) that, as we have just seen, can be read as "value pointed by", and the value pointed by
ted is indeed 25.

So, after all that, you may also infer (conclude) that for as long as the address pointed by
ted remains unchanged the following expression will also be true:

*ted == andy
Declaring variables of pointer types:

Pointer variable declarations


A variable that can hold pointers to other variables of type Type_Name is declared
similar to the
way you declare a variable of type Type_Name, except that you place an asterisk at the
beginning of the variable name.
Syntax: Type_Name*Variable_Name1, *Variable_Name2,. . .;
Example:
1 int * number;
2 char * character;
3 float * greatnumber;
These are three declarations of pointers. Each one is intended to point to a different data
type, but in fact all of them are pointers and all of them will occupy the same amount of
space in memory (the size in memory of a pointer depends on the platform where the
code is going to run). Nevertheless, the data to which they point to do not occupy the
same amount of space nor are of the same type: the first one points to an int, the second
one to a char and the last one to a float. Therefore, although these three example variables
are all of them pointers which occupy the same size in memory, they are said to have
different types: int*, char* and float* respectively, depending on the type they point to.

I want to emphasize that the asterisk sign (*) that we use when declaring a pointer only
means that it is a pointer (it is part of its type compound specifier), and should not be
confused with the dereference operator that we have seen a bit earlier, but which is also
written with an asterisk (*). They are simply two different things represented with the
same sign.

Now have a look at this code:

1 // my first pointer firstvalue is 10


2 #include <iostream.h> secondvalue is 20
3 int main ()
4 {
5 int firstvalue, secondvalue;
6 int * mypointer;
7
8 mypointer = &firstvalue;
9 *mypointer = 10;
10 mypointer = &secondvalue;
11 *mypointer = 20;
12 cout << "firstvalue is " << firstvalue <<
13 endl;
14 cout << "secondvalue is " <<
15 secondvalue << endl;
16 return 0;
}
Notice that even though we have never directly set a value to either firstvalue or
secondvalue, both end up with a value set indirectly through the use of mypointer. This is
the procedure:

First, we have assigned as value of mypointer a reference to firstvalue using the reference
operator (&). And then we have assigned the value 10 to the memory location pointed by
mypointer, that because at this moment is pointing to the memory location of firstvalue,
this in fact modifies the value of firstvalue.

In order to demonstrate that a pointer may take several different values during the same
program I have repeated the process with secondvalue and that same pointer, mypointer.
Here is an example a little bit more elaborated:

1 // more pointers firstvalue is 10


2 #include <iostream.h> secondvalue is 20
3 int main ()
4 {
5 int firstvalue = 5, secondvalue = 15;
6 int * p1, * p2;
7
8 p1 = &firstvalue; // p1 = address of
9 firstvalue
10 p2 = &secondvalue; // p2 = address of
11 secondvalue
12 *p1 = 10; // value pointed by p1 =
13 10
14 *p2 = *p1; // value pointed by p2 =
15 value pointed by p1
16 p1 = p2; // p1 = p2 (value of
17 pointer is copied)
18 *p1 = 20; // value pointed by p1 =
19 20
20
cout << "firstvalue is " << firstvalue <<
endl;
cout << "secondvalue is " <<
secondvalue << endl;
return 0;
}
I have included as a comment on each line how the code can be read: ampersand (&) as
"address of" and asterisk (*) as "value pointed by".

Notice that there are expressions with pointers p1 and p2, both with and without
dereference operator (*). The meaning of an expression using the dereference operator
(*) is very different from one that does not: When this operator precedes the pointer
name, the expression refers to the value being pointed, while when a pointer name
appears without this operator, it refers to the value of the pointer itself (i.e. the address of
what the pointer is pointing to).
Another thing that may call your attention is the line:

int * p1, * p2;

This declares the two pointers used in the previous example. But notice that there is an
asterisk (*) for each pointer, in order for both to have type int* (pointer to int).

Otherwise, the type for the second variable declared in that line would have been int (and
not int*) because of precedence relationships. If we had written:

int * p1, p2;

p1 would indeed have int* type, but p2 would have type int (spaces do not matter at all
for this purpose). This is due to operator precedence rules. But anyway, simply
remembering that you have to put one asterisk per pointer is enough for most pointer
users.

2.6.2. Pointers and arrays

The concept of array is very much bound to the one of pointer. In fact, the identifier of an
array is equivalent to the address of its first element, as a pointer is equivalent to the
address of the first element that it points to, so in fact they are the same concept. For
example, supposing these two declarations:

1 int numbers [20];


2 int * p;

The following assignment operation would be valid:

p = numbers;

After that, p and numbers would be equivalent and would have the same properties. The
only difference is that we could change the value of pointer p by another one, whereas
numbers will always point to the first of the 20 elements of type int with which it was
defined. Therefore, unlike p, which is an ordinary pointer, numbers is an array, and an
array can be considered a constant pointer. Therefore, the following allocation would not
be valid:

numbers = p;

Because numbers is an array, so it operates as a constant pointer, and we cannot assign


values to constants.
Due to the characteristics of variables, all expressions that include pointers in the
following example are perfectly valid:

1 // more pointers 10, 20, 30, 40, 50,


2 #include <iostream.h>
3 int main ()
4 {
5 int numbers[5];
6 int * p;
7 p = numbers; *p = 10;
8 p++; *p = 20;
9 p = &numbers[2]; *p = 30;
10 p = numbers + 3; *p = 40;
11 p = numbers; *(p+4) = 50;
12 for (int n=0; n<5; n++)
13 cout << numbers[n] << ", ";
14 return 0;
15 }

In the chapter about arrays we used brackets ([]) several times in order to specify the
index of an element of the array to which we wanted to refer. Well, these bracket sign
operators [] are also a dereference operator known as offset operator. They dereference
the variable they follow just as * does, but they also add the number between brackets to
the address being dereferenced. For example:

1 a[5] = 0; // a [offset of 5] = 0
2 *(a+5) = 0; // pointed by (a+5) = 0

These two expressions are equivalent and valid both if a is a pointer or if a is an array.

Pointer initialization

When declaring pointers we may want to explicitly specify which variable we want them
to point to:

1 int number;
2 int *tommy = &number;

The behavior of this code is equivalent to:

1 int number;
2 int *tommy;
3 tommy = &number;
When a pointer initialization takes place we are always assigning the reference value to
where the pointer points (tommy), never the value being pointed (*tommy). You must
consider that at the moment of declaring a pointer, the asterisk (*) indicates only that it is
a pointer, it is not the dereference operator (although both use the same sign: *).
Remember, they are two different functions of one sign. Thus, we must take care not to
confuse the previous code with:

1 int number;
2 int *tommy;
3 *tommy = &number;
that is incorrect, and anyway would not have much sense in this case if you think about it.
As in the case of arrays, the compiler allows the special case that we want to initialize the
content at which the pointer points with constants at the same moment the pointer is
declared:

char * terry = "hello";


In this case, memory space is reserved to contain "hello" and then a pointer to the first
character of this memory block is assigned to terry. If we imagine that "hello" is stored at
the memory locations that start at addresses 1702, we can represent the previous
declaration as:

It is important to indicate that terry contains the value 1702, and not 'h' nor "hello",
although 1702 indeed is the address of both of these.

The pointer terry points to a sequence of characters and can be read as if it was an array
(remember that an array is just like a constant pointer). For example, we can access the
fifth element of the array with any of these two expression:

1 *(terry+4)
2 terry[4]
Both expressions have a value of 'o' (the fifth element of the array).

2.6.3. Pointers to pointers

C++ allows the use of pointers that point to pointers, that these, in its turn, point to data
(or even to other pointers). In order to do that, we only need to add an asterisk (*) for
each level of reference in their declarations:
1 char a;
2 char * b;
3 char ** c;
4 a = 'z';
5 b = &a;
6 c = &b;

This, supposing the randomly chosen memory locations for each variable of 7230, 8092
and 10502, could be represented as:

The value of each variable is written inside each cell; under the cells are their respective
addresses in memory.

The new thing in this example is variable c, which can be used in three different levels of
indirection, each one of them would correspond to a different value:

 c has type char** and a value of 8092


 *c has type char* and a value of 7230
 **c has type char and a value of 'z'

You might also like