So far we have learned how to create variables to store information. But sometimes we need to store a lot of information. To do this, based on what we already know alone, we would need to create a lot of variables each with a different name. To make the problem of creating large numbers of variables more convenient, arrays were invented.
An array is a set of variables of one type (int or char or float, etc.). The variables in the array all have the same name but are differentiated by different index numbers (also called offsets or subscripts). Let's see how this is done.
We declare an array by writing the data type followed by the variable name and a set of brackets. Inside the brackets we write the number of variables we want. This number must be an integer literal.
Here is the general format of an array declaration:
data_type identifier [integer_literal];As a real example, we could write the following:
The different variables in the array are called elements of the array. These elements are located in contiguous spaces in memory, i.e. one after the other. In our example, the identifier "a" points to the first int in the set. By using the number 12 we have instructed the computer to find us 12 new integer locations in memory, all in one group location. In this case we specified 12 int's. If an int is 4 bytes then the computer will look for 12 * 4 or 48 bytes of memory in adjacent places (without any gaps between them). If we ask for a very large space, the computer might fail to find the space and the command will fail at run time. But usually you won't have this problem.
The name of this array will be "a". By itself, "a" holds the address in memory of the first int in the array called "a". In otherwords, the name of the array refers to the location in computer memory where the first element of the array resides. To get the contents of this memory space, i.e. the actual int that is at that place in memory, you need to de-reference the space. De-referencing means finding the content of a specific place in memory. We will discuss this more in the next chapter. For now just read on about how to actually do this de-referencing.
We can de-reference different parts of the array by using indexes, also called
offsets. The first element in any array is at the beginning of the
block of memory assigned to the array. We refer to it as being in place 0. In some
other programming languages it is referred to as being in place 1, but in C++ we start
counting array locations from 0 up. Here is how to
access the content of first or 0th element of the array and to
assign the element the value 18.
a[0] = 18 ;
Since we asked for 12 spaces, we got 12 spaces, but the indexes will
start with zero ( 0 ) and go until eleven ( 11 ). This simple point can confuse people.
Remember, when we declare an array, we write the total
number of elements we want. We do not write the index of the last element.
So, if we want to make an array of 12 elements, we declare it like this, for example,
int arr[12];
If we want to assign the first element the value 613 we would write:
arr[0] = 613 ;
And now if we want to assign the last element in the array the value 1625,
we would do the following.
arr[11] = 1625 ;
And similarly for all the middle elements of the array.
It would be a mistake to write this next line since there is no location 12th in the array.
arr[12] = 625 ; Wrong!
In fact, even though the above is an error, it would compile and that makes this error harder
to detect.
To reiterate, code which uses space beyond the end of the array will compile! But it
is always a logical error. So be careful!
A command like
arr[12] = 625 ;
or
arr[45] = 9925 ;
will compile, but we are then accessing a place in memory which we have no right
to access. Our program will compile, but will probably crash at run time.
This means that
if you do make this mistake, you may not discover it until your program fails to
run as expected.
We have now seen how in C++ we can create a large set of variables. This set, called an array can have any name we choose. All the elements must be of the same data type. We can access these variables by refering to their location within the array by an index number enclosed in brackets preceded by the array name.
One of the useful conveniences of having a set of variables with the same name and only differentiated by an index number is that we can use a loop to help us assign different values to the different elements in the array. We can increment through all the indexes of the array by using a counter as the index of the array.
For example,
if we wanted to keep track of the total fees we charged cars for
parking in our garage we would need a set of variables for the fees we charged the cars.
We would also need another variable for
the total daily charges. To do this we might write something like this.
double fee[ 12 ];
double total_fees = 0;
for(int counter =0; ! quit && counter < 12; counter++ )
{
cout<<"Enter a fee, 0 to quit";
cin>>fee[counter];
total_fees += fee[counter];
if (fee[counter] == 0)
{
quit = true;
}
}
In this example counter will start with the value of 0 and increment upwards
till it reaches 12 and then exit the loop. (Note: it will actually stop at 11
so we won't have a problem of overrunning the end of our array.)
The first car will have its fee
stored in the variable
fee [ 0 ].
The second car fee will be in fee[ 1 ] , and so on until the last or 12th
car will have its fee assigned to fee[11] .
If at some later time I wish to recall the fee of the 4th car I can
access it by writing fee [ 3 ] .
This provides a simple way to store a large set of data in an easily
accessible format.
If we didn't have arrays we would find this problem much harder to solve.
We would need to store each fee in a different variable. We could make
a bunch of variables like this
double fee, fee1,fee2,fee3,fee4,fee5;
Then we would have to copy each fee into the numbered fee variable for storing purposes.
We would have to use a lot of if's to check which
iteration number we were on so we would know which fee number to assign.
This would produce very messy code, but it could work.
Here is a possible way of doing this.
double fee,fee1,fee2,fee3,fee4,fee5;
bool quit = false;
short counter = 1;
double total_fees = 0;
while( ! quit && counter < 6 )
{
cout<<"Enter a fee, 0 to quit";
cin>>fee;
total_fees += fee;
if (fee == 0)
{
quit = true;
}
if( counter == 1 )
{
fee1 = fee;
}
if( counter == 2 )
{
fee2 = fee;
}
if( counter == 3 )
{
fee3 = fee;
}
if( counter == 4 )
{
fee4 = fee;
}
if( counter == 5 )
{
fee5 = fee;
}
counter++;
}
Now I can recall what the first or fifth (etc.) car paid, but
not only is this code long and overly complex, but also it would only work for
a maximum of 5
car fees. What if we wanted
to keep track of a thousand cars? The code would get unreasonably long.
To deal with problems such as this one, arrays were created.
There are two basic ways to initialize the values stored in an array.
The first way is to loop through all the values of the array and assign them some
value, like this:
long myArray[ 10 ];
for(int counter =0; counter < 10; counter++ )
{
myArray[ counter ] = 0;
}
As count goes up, the program works it way throught the array
assigning each element the value zero.
The other way is to define the initial values when you delcare the array.
If you want the all the elements of the array to have the value zero, you can do it
very simply,like this:
long myArray[ 10 ]={0};
But this will only work for the value zero, not for any other value.
So to initialize the array to the value 5 you need to do this:
long myArray[ 10 ]={5,5,5,5,5,5,5,5,5,5};
Or like this if you want different values in each element:
long myArray[ 10 ]={34,123,4355113,344,342,434,345,567,89,1238111};
This syntax is legal only when the array is first declared.
You cannot
at some later time use this syntax. For example the following is illegal:
long myArray[10];
myArray ={34,123,4355113,344,342,434,345,567,89,1238111};
In addition to these two methods, char arrays have a third way they can be initialized.
Instead of the cumbersome:
char myArray[ 10 ]={'H','e','l','l','o',' ','B','e','n','y'};
You can declare a char array and give it all the characters in one quotation.
Thus you may write:
char myArray[ 10 ]={"Hello Ben"};
Note that in the above example I only used 9 of my 10 array spaces. This is because if I use the double quote " " version of char array initialization the compiler will automatically add a zero (or null) as the final element in my array. If I were to insert "Hello Beny" then, since the sentence is 10 characters long, the zero would be placed in the 11th element of my array. Thus I would be over-reaching the end of my array. The zero at the end of the char array is useful, as we will see, in allowing cout to know where the end of the array is. A char array with a null character indicating the end of the word is called a string. We will discuss strings more in depth later.
You can also create an array of a much larger size than you need, and insert a
sentence in the beginning of it.
For example,
char myArray[ 100 ]={"Hello, my name is Ben."};
In this case myArray will only have its first 23 elements assigned. The other 77 will still contain junk. The first element will contain 'H' and the 23rd element will contain a 0 (or null).
There is one last way to declare and initialize arrays.
You can create an array of the exact size you need without actually counting your
elements. For example, if I want to store a set of integers in an array of type
long I can write the following:
long myArray[ ]={43,55113,344,342,434,345,567,89,1238111};
The computer will count the number of elements listed and, in this case,
create an array of 9 longs. But remember, I cannot extend the array size
later. I have created exactly 9 spaces and I cannot add more longs to this array
at any later time.
The same type of syntax exist for all array types. Here is an example using
a char array:
char myArray[ ]={"Hello, my name is Ben."};
This example will create an array of exactly 23 space, no more.
Another example
The name of the array in the above example is "myArray". What is the type of this name? It is not simply a char. It is a reference to a set of chars. As we have seen, if I want to dereference a particular element in the array I must specify which element. I do this by using the braces [ ] and including a number inside them. That number is called the index of the array, or the array offset.
But what if I wanted to pass an entire array to a function?
Before I can pass an array to a function I need to know
what type to write in the function definition?
Clearly, I would have to specify that it is an array and
not just a simple char. I would do this like this:
int myFunction ( char someName[] )
{
}
Or, if I am working with an array of doubles, I would write this:
int myFunction ( double someName[] )
{
}
This means that the function myFunction has a parameter which is an array
of doubles. Notice how there is no number inside the braces. This means
that the function can be passed an array of any size.
This is useful in a function where I do not know ahead of time
what size array I will be given.
I would call the function like this:
int main()
{
double myArray[120];
myFunction ( myArray );
}
The call will pass the whole array named myArray to the function myFunction.
Now the function can access all the element in the array.
In the function myFunction the elements will be referred to by the new
name I gave the array, someName. I.e. to print the
4th element in the array inside the function I would write
cout<< someName [ 3 ]<<endl;
A more complete function would look like this. This function will
print out all the elements of the array that is passed to it. Note: we
have to tell the function how large the array is, otherwise it won't know when to
stop printing.
int arrayPrint ( double arrayName[], int arraySize )
{
for(int i = 0; i < arraySize; i++ )
{
cout << arrayName[i] << endl;
}
}
We have learned that in functions any changes to the values that were passed as variables to the function (through the parameters) if changed inside the function, do not affect the original variable in the calling function. This is true because the variable itself is not passed to the function, but only the value of the variable is passed. That value is then copied into the variable which appears in the parameter list in the function defininition in the order it was written.
Thus, changes in the function variables do not affect the original variables. This is not true with arrays. Since an array name is a reference to the variables inside it, when a copy of this reference is made, the copy still refers to the same set of variables. In other words, the array name locates the real location where the array is stored in memory. So even if I make a copy of the address location, it is still referring to the same real place in the computer memory. So when I dereference the elements of the function's copy of the array name, I am accessing the same variables as the calling function's array. In summary, all you have to remember is this: Changes in the array elements in a passed array will affect the array in the calling function. This is because the name of the array tells me exactly where in the computer's memory the data is being stored, its address in memory. As a result, the function can go to the exact same place and change the data. Since the calling function will then access this same location in memory, it will see the changes the called function made.
As we know in general, a function receives a copy of the data passed to it. If it changes the copy then the original stays the same. But if the copy tells me the location of the original, then I can change the original. For example, if I give a hit-man an address of a person to be killed, even if the hit-man makes a copy of the address on a separate paper, when he goes to the house at the address on the copied paper and kills the man inside that house, the man who lives in the house is the same man as the one who lives at the address on the original paper. The address is the same after all. The paper is a copy, but the thing it refers to is the same one thing. This is how array parameter work.
Fortunately, we have seen that if we use the double quotes to initialize an array, a 0 will be inseted automatically. In other cases I have to make sure to insert the 0 myself if I plan to print the char array.
cin also has a special way of treating char arrays. cin can accept an entire array of chars from the keyboard. It will read until it sees a space or a null. It will also read in an entire double quoted sentence including any internal spaces. It can then insert this content into a pre-declared char array.
For example, here is a program segment which allows the user to assign
values to a char array.
char someWord[200];
cin >> someWord;
Characters inputted this way into an array will have a null character
placed at the end automatically. This mean that strings inputted via
cin can be printed by cout.
I can do the same thing with any two different data types.
For example, I can cast an int into a char, like this:
int a = 65;
char b = a;
cout<<b;
Explicit Casting is when you explicitly tell the compiler that you are casting.
For example I can print out a capital A like this:
int a =65;
char b = static_cast<char> (a);
cout<<b;
Or in old style C
int a =65;
char b = (char)a;
cout<<b;
But even more directly I can cast without assigning my value to a second variable.
For example:
int a =65;
cout<<static_cast<char> (a);
This is very useful when I have two integers and I want to do float mathematics.
For example, if I want to divide one int by another int but I want the result to be a
float (to preserve any decimal result). I should then cast one or both of the ints
to be floats and do the math. I will then get a float result.
For example, the following will print 2.5 instead of the normal int result of 2.
int a = 5;
int b = 2;
cout<<static_cast<float> (a)/b;
© Nachum Danzig November 2003 - December 2006