Skip to content

Introduction to Arrays

Chapter 8

Categories of Data Types

  1. Scalar (simple) types: Store a single logical value
    • int, double, char, bool, enum, etc.
  2. Homogeneous collections: Multiple values of the same type
    • arrays (which we are learning about here)
  3. Heterogeneous collections (structured types): Group related values of different types
    • struct (more on this category later in the semester)

Arrays

An array is a collection of a fixed number of elements (values), all of the same data type. A one-dimensional array has elements that are arranged in a list form.

Array Declaration

  • The syntax for declaring a one-dimensional array with SIZE elements:

    cpp
    dataType arrayName[SIZE];

    where SIZE is any constant expression that evaluates to a positive integer.

  • Here is an example declaration using a constant variable for the size.

    cpp
    // Creating an array of 24 doubles
    const int NUM_OF_STUDENTS = 24;
    double testScores[NUM_OF_STUDENTS];
  • We can create a new array with 8 elements:

    cpp
    int list[8];
    The array declared above has 8 consecutive locations in memory, each of which can store an integer value. Because nothing has been stored in the array yet, the initial values are unknown (could be anything).
    The array declared above has 8 consecutive locations in memory, each of which can store an integer value. Because nothing has been stored in the array yet, the initial values are unknown (could be anything).
  • The size of the array must be constant. The size of an array must be known at compile time.

WARNING

Make sure that all declared arrays have constant sizes that are known at compile time. For example, you cannot get the size from the user using cin and then declare the array of that size. In CSCI 315, you will learn an alternative method of declaring arrays with sizes that are unknown at compile time, but that is beyond the scope of this course.

Accessing Array elements

Just like with strings, we use the array subscript operator [] to access individual elements of the array.

  • Here is how to access a single array element, where indexExp is an expression with a non-negative integer value (called the index).

    cpp
    arrayName[indexExp];
  • The value of the index is the position of the item in the array.

  • The first element of an array is at index 0 (we start 0, not 1)!

    • Therefore, what is the last index in an array relative to its length?
  • We can store a value in this array:

    cpp
    list[4] = 100; // store 100 in the 5th element of the array
     is now stored in memory at index 4 of the array named list.
    100 is now stored in memory at index 4 of the array named list.
  • We can also access values from the array:

    cpp
    list[2] = 10;
    list[6] = 35;
    list[4] = list[2] + list[6];
    Setting and accessing array elements
    Setting and accessing array elements

Processing One-Dimensional Arrays

Examples of basic operations to perform on a one-dimensional array include:

  • Initializing
  • Inputting data
  • Outputting data stored in an array
  • Finding the largest and/or smallest element

Each operation requires the ability to step through elements of the array. This is easily accomplished using a loop.

  • Given the declarations:

    cpp
    const int LIST_SIZE = 100;
    int list[LIST_SIZE]; //array of size 100
  • Use a for loop to access array elements:

    cpp
    for (int iter = 0; iter < LIST_SIZE; iter++)
        cin >> list[iter];

Array Index Out of Bounds Error

The index of an array is in bounds if the index is >= 0 and < ARRAY_SIZE.

  • In C++, there is no guard against indices that are out of bounds.
  • If you index an array outside of its bounds, the program has undefined behavior. It might appear to work, produce unexpected results, or stop with a runtime error. C++ does not guarantee what will happen.

Array Initialization during Declaration

An array can be initialized when it is declared by listing the values inside curly braces. The array size is determined by the number of initial values in the braces.

Examples:

  • To declare an array with 5 elements and initialize them:

    cpp
    double sales[] { 12.25, 32.40, 16.90, 23, 46.84 };
  • An array can also be partially initialized during declaration:

    cpp
    double sales[10] {};

    Declares an array of 10 elements and initializes all of them to zero.

  • To declare a 10-element array and initialize sales[0] to 8, sales[1] to 5, and sales[2] to 12:

    cpp
    double sales[10] { 8.0, 5.0, 12.0 };

    All other elements are initialized to 0.

Aggregate Operations cannot be performed on Arrays in C++

Aggregate operation: any operation that manipulates the entire array as a single unit.

Most aggregate operations are NOT possible with arrays. For example:

cpp
const int LIST_SIZE = 5;
int myList[LIST_SIZE] { 1, 5, 3, 7, 16 };
int yourList[LIST_SIZE];
yourList = myList; // error: invalid array assignment

Solution: use a loop.

cpp
// Traditional for loop
for (int index = 0; index < LIST_SIZE; ++index)
    yourList[index] = myList[index];

Auto Variable Types

Remember, C++11 allows the auto declaration of variables.

  • Data type does not need to be specified when initializing a variable at declaration.

    cpp
    auto num1 = 15; // num1 is an int because 15 is an int.
    auto num2 = 15.0; // num2 is a double
    auto num3 = 'A'; // num3 is a char
    auto num4 = num1; // num4 is an int
  • The auto keyword is a placeholder for a type, but it is not itself a type. The compiler determines the type based on the value it receives.

  • There is no accidental conversion between types (implicit casting), so your code is not only more flexible but possibly more efficient.

Range-Based For Loops

We have already used range-based for loops with strings. They may also be used with arrays.

Example:

cpp
double list[] { 45.3, 77.8, 103.9, 84.2, 88.5, 109.6, 99.5, 101.2, 31.8, 99.2 };
auto sum = 0.0; // a double

// Calculate the sum of all the elements in the list array.
for (auto num : list) // For each num in list
    sum = sum + num;

In the first part of this for loop, the variable, num, holds a copy of an element from the array (list) that is the second part. Range-based loops are very clean because you do not need to index the values in the array. However, you are limited to traversing the entire array from beginning to end.

WARNING

Range-based for loops work when the compiler can determine the beginning and end of the range. A raw array parameter does not retain the array's length: it is adjusted to a pointer, so a range-based loop cannot be used directly on it. You can use a separate length parameter with an indexed loop, or use a span (introduced later in this note).

Arrays as Parameters to Functions

  • Array arguments are not copied when passed to a function. In a parameter list, an array parameter is adjusted to a pointer to its first element. This allows the function to modify the original array.

    • Do not use the & symbol when declaring an array parameter in this form.
  • The length of the array should be omitted from the square brackets. If provided, it is ignored by the compiler. If the function needs to know the length of the array, it must be passed as an additional parameter.

  • Example:

    cpp
    void funcArrayAsParam(int arrayOne[], double arrayTwo[], int length);
  • To prevent a function from changing the array elements, use const in the declaration of the parameter.

    • Example:

      cpp
      void exampleFunc(const int VALS[], int size);
  • C++ does not allow functions to return an array. You can get around this limitation using something known as pointers, but that topic will be discussed in a later course.

TIP

Important: If an array parameter is not to be modified inside the function, make it a const array parameter.

Base Address of an Array and Array in Computer Memory

The base address of an array: address (memory location) of the first array element

  • Give a one-dimensional array named list, its base address is the address of list[0].

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

  • You can view the Base address for the array like this:

    cpp
    int array[5]; // Declare array with 5 elements
    cout << array; //  Display the array’s base address (no square brackets)
    
    // The address of the first element matches the array's base address.
    cout << &array[0];
  • The base address is commonly displayed in hexadecimal notation (for example, 0x1234CC00). Hexadecimal is the base-16 numbering system and has sixteen distinct symbols for each digit: 09 and AF. Each hexadecimal digit represents four binary digits (bits).

A comparison of the small integers in different bases.
Base 20110111001011101111000100110101011110011011110111110000
Base 10012345678910111213141516
Base 160123456789ABCDEF10

Common Errors with Arrays

  1. Arrays in C++ (and almost all programming languages) are zero-indexed. That means that the first index is zero, not one. The last index is the length minus one. It is easy to have an off-by-one error if you forget this fact.
  2. Array index out of bounds: do not access an index outside the range 0 through length - 1. Doing so produces undefined behavior, not a guaranteed error message.
  3. Array arguments are not copied when passed to a function, so changes to the elements persist after the function call. Use const to prevent a function from modifying the elements.
  4. Can’t set one array equal to another with just the equal operator. Use a loop instead.
  5. A function cannot return a built-in array by value. Arrays are not copied when passed to functions, so a function can usually fill an array supplied by its caller instead.

The span Container for Arrays

In C++20 (the C++ standard released in 2020), a helpful view called span was added to associate an array with its length/size. An array is an object that contains its elements, but when an array is passed to a function using a raw array parameter, it is adjusted to a pointer to its first element. That pointer does not keep track of how many elements are in the array. As a result, we often need to pass the array size to a function parameter as an additional parameter. For example,

cpp
void display (const double NUMBERS[], int length)
{
    cout << "Length: " << length << '\n'; // display the array length
    for (int index = 0; index < length; ++index)
    {
        cout << NUMBERS[index] << '\n';
    }
}

The span container allows us to connect the array with its size. For example,

cpp
/*
 * Demonstration of using span to connect an array with its size.
 * To compile with the C++20 standard, add the following command line argument
 * -std=c++20
 * (Replace -std=c++20 with -std=c++2a in GCC 9 or earlier.)
 */
#include <iostream>
#include <span> // To use this new span container
using namespace std;

/**
 * Displays the length and values in an array of doubles.
 * The numbers parameter is a constant span for an array of doubles.
 *     Like static_cast, the type goes within the <>. If you change
 *     <double> to <int>, then it will accept an array of integers.
 */
void display (const span<double> numbers);

int main()
{
    // Create an example array of length 10.
    const int LENGTH = 10;
    double array[LENGTH] { 1.1, 2.1, 3.1, 4, 5.1, 6.1, 7.1, 8.1, 9.1, 10.1 };

    // Display the array using an implicit cast to a span container.
    display(array);

    return 0;
}

void display (const span<double> numbers)
{
    // Display the array length
    cout << "Length: " << numbers.size() << '\n';

    // Display each number. This range-based for loop would not work with a
    // traditional array parameter.
    for (auto num : numbers)
    {
        cout << num << '\n';
    }
}