University of Leeds

Arrays

Introduction

A variable represents a single location in memory. A collection of variables in a contiguous address space is known as an array. The behaviour of arrays and pointers are closely related. A diagram of how an integer array may be stored in memory is shown below.

Diagram showing memory address space.

This example represents an integer array with 7 elements named array. Note that the arrays are zero-indexed i.e. start from 0. All the elements in the array appear in adjacent memory locations and take up one row each. Individual elements of the array can be accessed using the square brackets and passing in the relevant index.

Static arrays

Arrays in which the contents of the array are known at compile-time are known as static arrays. When creating a static array, the size must be known and then cannot be changed. The size (or length) can be explicitly declared when creating, or can be inferred from the number of initialisation values provided. Note that it is not possible to use a variable to specify the size of an array, you must use a literal value. The example below demonstrates how to create an array.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#include <iostream>

int main() {
  // initialse an array with values
  int array[5] = {100, 101, 102, 103, 104};
  // iterate over the array and print the values
  for (int i = 0; i < 5; i++) {
    std::cout << "array[" << i << "] = " << array[i] << std::endl;
  }
  // since C++11, we can use range-based for loops. Note we don't
  // have to know the length of the array, however we don't
  // automatically get the array index like a traditional
  // for loop
  for (int value : array) {
    std::cout << value << std::endl;
  }
}

Here we can see that we have created an integer array with 5 values. We could have actually left the square brackets empty as we have provided 5 initial values using a braced initialiser list. Arrays of other datatypes can also be created by substituting for the relevant keyword (e.g. char or double).

Once you have an array, it is very common to find yourself having to iterate over the values in the array using a for loop. It is important that you know the size of the array so you can configure the for loop with the relevant conditional statement. C++11 introduced range-based for loops in which the size of the array can be automatically determined. Although this has its advantages, it unfortunately does not give you direct access to the loop index (e.g. i in this example). The output of the code is shown below.

array[0] = 100
array[1] = 101
array[2] = 102
array[3] = 103
array[4] = 104
100
101
102
103
104

Dynamic arrays

As well as having fixed-length arrays that are created at compile-time, it is also possible to create arrays whose lengths are determined at run-time. These are known as dynamic arrays. The example below shows how to create a dynamic array.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#include <iostream>

int main() {
  // when we dynamically create an array, we can specify the length
  // of the array using a variable
  int n = 10;
  int *array = new int[n];

  // iterate over the loop and set the elements to some values
  for (int i = 0; i < n; i++) {
    array[i] = i * i;
    std::cout << "array[" << i << "] = " << array[i] << std::endl;
  }
  // when we've finished, delete the memory and null the pointer to be safe
  delete[] array;
  array = nullptr;
}

Here we create an integer pointer and create the array using the new operator which will allocate enough memory for an array of the requested size. We can then treat the array the same as previously and iterate over it etc. When creating a dynamic array, it is very important to free up the memory using delete to prevent memory leaks occurring. It can also be good practice to null the pointer to the array just to ensure that it cannot be accidentally used to access the memory later. The output of the code is below.

array[0] = 0
array[1] = 1
array[2] = 4
array[3] = 9
array[4] = 16
array[5] = 25
array[6] = 36
array[7] = 49
array[8] = 64
array[9] = 81

Arrays and pointers

There is a close relationship between pointers and arrays due to the fact that they are implicitly linked to memory addresses. Although we used to accessing array elements using the subscript index [], we can also do so via pointers. Some example code is below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>

int main() {
  // initialse an array with values
  int array[5] = {100, 101, 102, 103, 104};
  // iterate over the array and print the values
  for (int i = 0; i < 5; i++) {
    std::cout << "array[" << i << "] = " << array[i] << std::endl;
  }
  // print out the address of each array element
  for (int i = 0; i < 5; i++) {
    std::cout << "address of array[" << i << "] = " << &array[i] << std::endl;
  }
  // array is equivalent to &array[0]
  std::cout << "array = " << array << std::endl;
  // we can use pointers to access the array elements
  for (int i = 0; i < 5; i++) {
    std::cout << "*(array+" << i << ") = " << *(array + i) << std::endl;
  }
}

The output of the code is below.

array[0] = 100
array[1] = 101
array[2] = 102
array[3] = 103
array[4] = 104
address of array[0] = 0x7ffedfde6b70
address of array[1] = 0x7ffedfde6b74
address of array[2] = 0x7ffedfde6b78
address of array[3] = 0x7ffedfde6b7c
address of array[4] = 0x7ffedfde6b80
array = 0x7ffedfde6b70
&array[0] = 0x7ffedfde6b70
*(array+0) = 100
*(array+1) = 101
*(array+2) = 102
*(array+3) = 103
*(array+4) = 104

The code creates a static array and prints the address of each array element using the & operator. Note how the addresses go up by 4 each time due to the 32-bit (4 byte) size of an integer. The code then prints array without any subscript. It turns out that array is just the address of the first element in the array i.e. a pointer. We can then get access to the first value stored in the array using the de-reference operator *array. By incrementing this pointer, we can get access to later elements in the array. Hence array[i] is equivalent to *(array+i).

Passing arrays to functions

Arrays can also be passed to (and returned from) functions, just as variables can be. An example is below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>

int sum_of_array(const int array[], int n);
void square_array(int array[], int n);

int main() {
  // create array and initialise
  int array[5] = {1, 2, 3, 4, 5};
  // pass the array to the function (and its size) so we can calculate the sum
  int sum = sum_of_array(array, 5);
  std::cout << "Sum = " << sum << std::endl;
  // now pass to a function to square each value
  square_array(array, 5);
  for (int i = 0; i < 5; i++) {
    std::cout << "array[" << i << "] = " << array[i] << std::endl;
  }
  // calculate the new sum
  sum = sum_of_array(array, 5);
  std::cout << "New sum = " << sum << std::endl;
}
// note that the array is described as const here so that the function cannot
// modify the contents
int sum_of_array(const int array[], int n) {
  int sum = 0;
  for (int i = 0; i < n; i++) {
    sum += array[i];
  }
  return sum;
}
// this function needs to modify the array and so isn't const
void square_array(int array[], int n) {
  for (int i = 0; i < n; i++) {
    array[i] = array[i] * array[i];
  }
}

The example has two functions, one to calculate the sum of an array and the other to square each element in an array. Each function accepts an array as a argument. Note that the subscript [] is used in the list of function arguments to tell the compiler that an array is being passed in. Also note that we must pass in the length of an array, as the function cannot automatically determine the length.

For the sum function, we do not want the function to modify the contents of the array. We therefore use the const keyword in front of the function argument. This prevents the function from being able to modify it. You can think of it as the function having only read-only access to the array. However, for the square function, we do want the function to be able to modify the values and hence we do not use the const keyword.

The output is below.

Sum = 15
array[0] = 1
array[1] = 4
array[2] = 9
array[3] = 16
array[4] = 25
New sum = 55