Back to CEO's Home Page

Arrays and Strings

Charles E. Oyibo

We will recall that a data type is called simple if variables of that type can store only one value at a time. In constrast, in a structured data type each data item is a collection of other data items. Simple data types are the building blocks of structured data types. Arrays, are a structured data type.

Arrays

An array is a collection of fixed number of components all of the same data type. A one-dimentional array is an array in which the components are arranged in a list form. We will concentrate on one-dimentional arrays for now.

The general form for declaring a one-dimentional array is:

dataType arrayName[intExp];

where intExp is a constant expression that evaluates to a positive integer. Also, intExp specifies the number of components in the array.

For example, the statement:

int num[5];

declares an array num of 5 components. Each component is of the type int. The components are num[0], num[1], num[2], num[3], num[4]. In other words, we have declared 5 variables, all of the type int.

Assessing Array Components

The general form (syntax) used for assessing an array component is:

arrayName[indexExp]

where indexExp, called the index, is any expression whose value is a non-negative integer. The index value specifies the position of the component in the array.

Recall that in C++, [] is called the array subscripting operator. Moreover, note that in C++, the array index starts at 0

Consider:

int list[10]; // Line1

list[5] = 34; // Line2

i = 4;
list[i] = 63 // Line 3


list[2 * i - 2] = 58 // Line 4

Line 1 declares an array list of 10 components (list[0], list[1], ... , list[9]); line 2 stores 34 in list[5]; line 3 stores 63 to list[4]; line 4 stores 58 to list[6].

We can also declare arrays thus:

const int arraySize = 10;

int list[arraySize];

Observe that arraySize is declared as a constant.

When we declare an array, its size must be known. That is, we may not declare an array as a variable, thus:

int arraySize; //Line 1

cout << "Enter the size of the array: " //Line 2
cin >> arraySize; //Line 3
cout << endl; //Line 4

int list[arraySize] //Line 5

Because when the compiler compiles Line 1, the value of the variable arraySize is unknown. Thus when the compiler compiles Line 5, the size of the array is unknown and the compiler will not know how much memory to allocated for the array.

We will explore how to specify the size of an array during program execution, and then declare an array of that size using pointers. Arrays that are created during program execution are called dynamic arrays. For now though, whenever we declare an array, we must specify its size (i.e. the number of elements is has).

Processing One-Dimentional Arrays

Some of the basic operations performed on a one-dimentional array are:

and if the data is numeric,

Each of these operations require the ability to step through the elements of the array. This is accomplished using a loop.

Consider the following statements:

double sales[10];
int index;
double largestSale, sum, average;

The following lines of code...

...initialize every component of the array sale to 0.0:

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

    sale[index] = 0.0;

...read data into the array sale (we assume the data is entered at the keyboard):

for (index = 0; index < 10; index++)
    cin >> sale[index];

...output the array sale (we assume the output goes to screen):

for (index = 0; index < 10; index++)
    cout << sale[index] << " ";

...find the sum and average of the elements of the array sale:

sum = 0;

for (index = 0; index < 10; index++)
    sum = sum + sale[index];

average = sum / 10;

...determine the index of the largest element in the array sale:

maxIndex = 0;

for (index = 1; index < 10; index++)
    if (sale[maxIndex] < sale[index])
       maxIndex = index;

largestSale = sale[maxIndex];

Explanation: Initially, we assume that the first element in the list is the largest element and so maxIndex is initialized to 0. We then compare the element pointed to by maxIndex with every subsequent element in the list. Whenever we find an element in the array larger than the element pointed to by maxIndex, we update maxIndex so that it points to the new larger element.

Array Index Out of Bounds

Consider:

double num[10];

A loop such as:

for (i = 0; i <= 10; i++)
    num[i] = 0;

can set the index out of bounds. When i becomes 10, the loop test condition i <= 10 evaluates to true and the body of the loop executes, which results in storing 0 in list[10]. Logically, list[10] does not exist.

Formally, the index (say, index) of an array is in bounds if:

  1. index >= 0, and
  2. index <= arraySize - 1.

Otherwise, the index is out of bounds. If during program execution, the index goes out of bounds, several unexpected things can happen. Since C++ has no in-built safe-guards against out-of-bound indices, it is incumbent on us, the programmers, to ensure that the index value is within range.

Array Initialization During Declaration

The following statement declares an array, sales, of five components and initializes these components.

double sales[5] = {12.25, 32.43, 23, 24.55, 30.6};

Here:

sales[1] = 12.25
sales[2] = 32.43
sales[3] = 23.00
sales[4] = 24.55
sales[5] = 30.60

When initializing arrays while declaring them, it is not necessary to specify the size of the array. The size is determined by the number of initial values in the braces. We must, however, still include the (empty) brackets ([]) following the array name. Hence, our statement above could legally take the form:

double
sales[] = {12.25, 32.43, 23, 24.55, 30.6}

Partial Initialization of Arrays During Declaration

The statement

int list[10] = {5};

declares list of be an array of 10 components and initializes the first component to 5 and the rest to 0. The statement:

int list[10] = {8, 5, 12};

declares list to be an array of 10 components, initializes list[0] to 8, list[1] to 5, list[2] to 12, and all the other components to zero. Hence, if all the values are not specified in the initialization statement, the array components for which the values are not specified are initialized to 0.

Note that the statement:

int list[] = {5, 6, 3};

declares list to be an array of 3 components and intilizes list[0] to 5, list[1] to 6, and list[2] to 3. In contrast, the statement:

int list[25] = {4, 7};

declares list to be an array of 25 components, initializing the first two components to 4 and 7 respectively, and the remaining to 0.

Some Restrictions of Array Processing

C++ does not allow aggregate operation on an array (an aggregate operation on an array being any operation that is, at least, intended to manipulate the entire array as a unit). To, say, copy one array into another, we must copy it component-wise -- that is, one component at a time. We could use the following loop to accomplish this.

for (j = 0; j < 25; j++)
    y[j] = x[j];

rather than simply

y = x;

where x and y are both arrays of size 25.

Arrays as Parameters to Functions

How are arrays passed as parameters to functions?

By reference only: In C++, arrays are passed by reference only.

Moreover, since arrays are passed by reference only, we do not use the symbol & when declaring an array as a formal parameter. Furthermore, when declaring a one-dimentional array as a formal parameter, we omit the size of the array.

Consider:

void functionArrayAsParam(int listOne[], double listTwo[])
{
...
}

The function functionArrayAsParam has two formal parameters: (1) listOne, a one-dimensional array of the type int (i.e. its components are of the type int); and (2) listTwo, a one-dimentional array of the type double. In this declaration, the size of both arrays are unspecified.

Consider a function of that is intended to initialize all the elements of an int-type array of any size to 0:

void initialize(int list[], int listsize)
{
     int count;
     for (count = 0; count < listSize; count++)
         list[count] = 0;
}

The first parameter of the function initialize is an int array of any size. When the function intialize is called, the size of the actual array is passed as the second parameter of the function intialize.

Constant Arrays as Formal Parameters

Even though an array is always passed by reference, we can still prevent the function from changing the actual parameter(s). We do so by using the reserved word const in the declaration of the formal paramter, as in:

void example(int x[], const int y[], int sizeX, int sizeY)
{
...
}

Here the function example can modify array x, but not array y. Any attempt to change y will result in a compile-time error.

It is a good programming practice to declare an array to be a constant as a formal parameter if we do not want the function to modify the array.

... examples that show how to write functions for array processing and declare an array as a formal parameter...

Base Address of an Array

The base address of an array is the address (i.e. memory location) of the first array component. E.g. if list is a one-dimentional array, then the base address of list is the address of the component list[0].

When we pass an array as a parameter, the base address of the actual array is passed to the formal parameter.

Question: Why do we pass arrays as reference parameters rather than as value parameters.

Answer: If we were to pass arrays as value parameters, the array would have to be copied from the actual parameters in the function call to the formal parameters in the function definition. Not only does this have the potential for taking a considerable amount of computer time, memory also has to be allocated to hold essentially redudant data. This would evidently make for a very slow and inefficient program. To be more efficient, we pass arrays as reference parameters, so that only the base address of the array is passed to the function.

Functions Cannot Return a Value of the Type array

C++ does not allow functions to return a value of the type array.

...

Integral Data Type and Array Indices

...

Other Ways to Declare Arrays

...

c-strings (Character Arrays)

Character arrays are of special interest and we process them differently than other arrays. C++ provide a number of (predefined) functions that we can use with character arrays.

A character array is an array whose components are of the type char.

... Subtopics:

Parallel Arrays

Two (or more) arrays are called parallel if their corresponding components hold related information.

We may, for example, be interested in keeping track of students' course grades, together with their ID numbers. To do this, we could declare two arrays: studentId of the type int and courseGrade of the type char (let's assume there are 50 students in our class):

int studentId[50];
char courseGrade[50];

Suppose that the input file is opened using the ifstream variable infile. We make the following considerations:

We implement the following loop, then, to read the data into the parallel arrays studentId and courseGrade.

int noOfStudents = 0;

infile >> studentID[noOfStudents] >> courseGrade[noOfStudents];

while (infile && noOfStudents < 50)
{
    noOfStudents++
    infile >> studentID[noOfStudents] >> courseGrade[noOfStudents];
}

Again, the two conditions in the while loop ensure that (1) the program does not attempt to read past the end of file, and (2) we do not attempt to write past the end of the array.

Two- and Multidimensional Arrays

We have thus far dealt with data presented as lists (one-dimentional arrays). Data presented in table form can be manipulated in C++ using two-dimentional arrays.

A two-dimentional array is, formally, a collection of a fixed number of components arranged in rows and columns (that is, in two dimentions), wherein all components are of the same type.

The syntax for declaring a two-dimentional array is:

dataType arrayName[intExp1][intExp2];

where intExp1 and intExp2 are constant expressions yielding positive integer values. The two expressions intExp1 and intExp2 specify the number of rows and the number of columns, respectively, in the array. The statement

double sales[10][5];

declares a two-dimentional array sales of 10 rows and 5 columns, where every component is of the type double. As in the case of one-dimentional arrays, the rows are numberd 0...9 and the columns are numbered 0...4.

Accessing Array Components

To access the components of a two-dimentional array, we need a pair of indices: one for the row position and one for the column position. The syntax:

arrayName[indexExp1][indexExp2]

where indexExp1 and indexExp2 are expressions yielding non-negative integer values. indexExp1 specifies the row position; indexExp2 specifies the column position.

Suppose that: int i = 5; and int j = 3;

Then the statements: sales[5][3] = 25.75; and sales[i][j] = 25.75; both do the same thing: store 25.75 into row number 5 and colum number 3 (i.e. the 6th row and 4th column) of the array sales.

Two-Dimentional Array Initialization During Declaration

Consider the following statement:

int board[4][3] = {{2, 3, 1}, {15, 25, 13}, {20, 4, 7}, {11, 18, 14}};

This statement declares board to be a two-dimentional array of 4 rows and 3 columns. The components in the first row are 2, 3, and 1; the components of the second row are 15, 25, and 13; the components of the third row are 20, 4, and 7; and the components of the fourth row are 11, 18, and 14, respectively.

To initialize a two-dimentional array when it is declared:

  1. The elements of each row are enclosed within braces and separated by commas.
  2. All rows are enclosed within braces.
  3. For number arrays, if all components of a row are not specified, the unspecified components are initialized to 0. In this case, at least one of the values must be given to initialize all the components of a row.

Two-Dimentional Arrays and Enumeration Types

We can also use the enumeration type for array indices. Consider the following statements:

const int numberOfRows = 6;
const int numberOfColumns = 5;

enum carType {GM, Ford, Toyota, BMW, Nissan, Volvo};
enum color {Red, Brown, Black, White, Gray};

int inStock[numberOfRows][numberOfColumns];

These statements define the carType and colorType enumeration types, and define inStock as a two-dimentional array of 6 rows and 5 columns. Suppose that each row in inStock corresponds to a car type, and each column corresponds to a color. Suppose, further, that each entry in inStock represents the number of cars of a particular type and color.

inStock
  [Red] [Brown] [Black] [White] [Gray]
[GM]          
[Ford]          
[Toyota]          
[BMW]          
[Nissan]          
[Volvo]          

The statement:

inStock[1][3] = 15;

is equivalent to

inStock[Ford][White] = 15;

Observe that the second statement easily conveys the message--that is, set the number of White Ford cars to 15. This illustrates that enumeration types can be used effectively to make the program readable and easy to manage.

Processing Two-Dimentional Arrays

A two-dimentional array can be processed in three ways:

  1. Process the entire array.
  2. Process a particular row of the array, called row processing.
  3. Process a particular column of the array, called column processing.

Consider the following declarations:

const int noOfRows = 7;
const int noOfCols = 6;
int matrix[noOfRows][noOfCols];
int row;
int col;
int sum;
int largest;
int temp;

Initialization

The following for loop initializes initialize row number 4 (that is, the 5th row) to 0:

row = 4;
for (col = 0; col < noOfCols; col++)
    matrix[row][col] = 0;

If we want to initialize the entire matrix to 0, we could say:

for (row = 0; row < noOfRows; row++)
    for (col = 0; col < noOfCols; col++)
        matrix[row][col] = 0;

Print

The following nested for loop prints the components of matrix, one row per line:

for (row = 0; row < noOfRows; row++)
{
    for (col = 0; col < noOfCols; col++)
        cout << setw(5) << matrix[row][col] << " ";

    cout << endl;
}

Input

The following for loop inputs the data in row number 4 (i.e. the 5th row) of matrix:

row = 4;
for (col = 0; col < noOfCols; col++)
    cin >> matrix[row][col];

As before, by nesting the "col" loop inside the "row" loop, we could input data into each component of matrix:

for (row = 0; row < noOfRows; row++)
    for (col = 0; col < noOfCols; col++)
        cin >> matrix[row][col];

Sum by Row

The following for loop finds the sum of row number 4 of matrix; that is, it adds the components of the 5th row:

sum = 0;
row = 4;
for (col = 0; col < noOfCols; col++)
    sum = sum + matrix[row][col];

Again, by putting the row number in the loop, we can find the sum of each row separately:

//Sum of each individual row
for (row = 0; row < noOfRows; row++)
{
    sum = 0;
    for (col = 0; col < noOfCols; col++)
        sum = sum + matrix[row][col];

    cout << "Sum of row " << row + 1 << " = " << sum << endl;
}

Sum by Column

As in the case of sum by row, the following for loops finds the sum of each individual columnL

//Sum of each individual column
for (col = 0; col < noOfCols; col++)
{
    sum = 0;
    for (row = 0; row < noOfRows; row++)
        sum = sum + matrix[row][col];

    cout << "Sum of column" << col + 1 << " = " << sum << endl;
}

Largest Element in Each Row and Each Column

The following for loop determines the largest element in row number 4:

row = 4;
largest = matrix[row][0]; //we assume that the first element of the row is the largest

for (col = 0; col < noOfCols; col++)
    if (largest < matrix[row][col])
        largest = matrix[row][col];

The following C++ code determines the largest element in each row and each column:

//largest element in each row
for
(row = 0; row < noOfRows; row++)
{
    largest = matrix[row][0]; //we assume that the first element of the row is the largest

    for (col = 0; col < noOfCols; col++)
        if (largest < matrix[row][col])
            largest = matrix[row][col];

    cout << "The largest element in row " row + 1 << " = " << largest << endl;
}

//largest element in each column
for
(col = 0; col < noOfCols; col++)
{
    largest = matrix[row][0]; //we assume that the first element of the column is the largest

    for (row = 0; row < noOfRows; row++)
        if (largest < matrix[row][col])
            largest = matrix[row][col];

    cout << "The largest element in row " row + 1 << " = " << largest << endl;
}

Reversing Diagonal

Suppose that matrix is a square array (i.e. noOfRows and noOfCols are the equal). Then matrix has a main diagonal and an opposite diagonal. Let's suppose that we want to reverse both the diagonals of matrix, and that we have:

const int noOfRows = 7;
const int noOfCols = 6;

To reverse the main diagonal, we want to:

  1. Swap matrix[0][0] with matrix[3][3]
  2. Swap matrix[1][1] with matrix[2][2]

And to reverse the opposite diagonal, we want to:

  1. Swap matrix[0][3] with matrix[3][0]
  2. Swap matrix[1][2] with matrix[2][1]
  [0] [1] [2] [3]
[0] [0][0]     [0][3]
[1]   [1][1] [1][2]  
[2]   [2][1] [2][2]  
[3] [3][0]     [3][3]

The following for loop reverses the diagonals:

// reverse the main diagonal
for (row = 0; row < noOfRows / 2; row++)
{
    temp = matrix[row][row];
    matrix[row][row] = matrix[noOfRows - 1 - row][noOfRows - 1 - row];
    matrix[noOfRows - 1 - row][noOfRows - 1 - row] = temp;
}

// reverse the opposite diagonal
for (row = 0; row < noOfRows / 2; row++)
{
    temp = matrix[row][noOfRows - 1 - row];
    matrix[row][noOfRows - 1 - row] = matrix[noOfRows - 1- row][row];
    matrix[noOfRows - 1 - row][row] = temp;
}

The preceding C++ code to reverse the diagnonals of a square, two-dimentional array works for an array of any size.

Passing Two-Dimentional Arrays as Parameters to Functions

Two-dimentional arrays are passed as reference parameters to functions, with the base address (the address of the first component of actual parameter) being passed to the formal parameter.

When sorting a two-dimentional array in the computer's memory, C++ uses the row order form. That is, the first row is sorted first, followed by the second row, and so on.

Recall that when declaring a one-dimentional array as a formal parameter, we usually omit the size of the array. Because C++ sorts two-dimentional arrays in row order form, the compiler must know where one row ends and where the next row begins in order to computer the address of a component correctly. Thus, when declaring two-dimentional arrays as a formal parameter, we can omit the size of the first dimention, but not the second; that is, we must specify the number of columns.

Suppose we have:

const int noOfRows = 6;
const int noOfCols = 5;

Consider the following definition of the function printMatrix:

void printMatrix(int[][noOfCols], int noOfRows)
{
    int row, col;

    for (row = 0; row < noOfRows; row++)
    {
        for (col = 0; col < noOfCols; col++)
            cout << setw(5) << matrix[row][col] << " ";

        cout << endl;
    }
}

This function takes as a parameter a two-dimentional array of an unspecified number of rows, and 5 columns, and outputs the content of the two-dimentional array. During the function call, the number of the actual paramters must match the number of the columns of the formal paramter.

Array of Strings

Suppose we need to perform an operation, such as alphabetizing a list of names. Because every name is a string, a convenient way to stire the list of names is to use an array. Strings in C++ can be manipulated using either the data type string or character arrays (c-strings).

Arrays of Strings and the string Type

Suppose that the list consists of a maximum of 100 names. We can declare an array of 100 components of the type string as follows:

string list[100];

Basic operations like assignment, comparison, and input/output can be performed on values of the string type.

Arrays of Strings and c-Strings (Character Arrays)

Suppose that the largest string in our list is 15 characters long and out list has 100 strings. We can declare a two-dimentional array of characters of 100 rows and 16 columns as follows:

char list[100][16];

So that list[j] for each j, 0 <= j <= 99, is a string of at most 15 characters in lenght.

We set the columns to be 16 because, as we will recall, the \0 character marks the end of a string.

Suppose we want to read and store data in our array list and that there is one entry per line, the following loop accomplishes this task:

for (j = 0; j < 100; j++)
    cin.get(list[j], 16);

The following outputs the string in each row:

for (j = 0; j < 100; j++)
    cout << list[j] << endl;

Another Way to Declare a Two-Dimentional Array

If we know the size of the tables with which the program will be working, then we can use typedef to first define a two-dimensional array data type and then declare variables of that type. For example, consider:

const int numOfRows = 20;
const int numOfCols = 10;

typedef int tableType[noOfRows][noOfCols];

The previous statement defines a two-dimensional array data type tableType. Now we can declare variables of this type. So:

tableType matrix;

declares a two-dimentional array matrix of 20 rows and 10 columns.

...

Multidimensional Arrays

Recall that a 1-dimensional array is an array in which the elements are arranged in a list form and that a 2-dimensional array is one in which the elements are arranged in a table form...

A general definition of an array is in order here:

An array is a collection of a fixed number of elements (called components) arranged in n dimentions (n >= 1), called an n-dimentional array.

The general syntax for declaring an n-dimensional array is:

dataType arrayName[intExp1][intExp2] ... [intExpn];

where intExp1, intExp2, and intExpn are constant expressions yielding positive integer values.

The syntax to access the components of an n-dimentional array is:

arrayName[indexExp1][indexExp2] ... [indExpn]

where intExp1, intExp2, and intExpn are expressions yielding non-negative integer values. intExpi gives the position of the array in the ith dimension.

...

Top of page

Contact Information

Page Last Updated: Saturday February 12, 2005 10:21 AM