07: Arrays

UC Irvine - Fall ‘22 - ICS 45C

Quick list of things I want to talk about:

  • Homogeneously typed
  • Fixed size

Expanded notes:

Now that we can execute our statements many times, it would be nice to be able to store many items so we can iterate through them. An easy way to do that is to use arrays!

An array is an ordered collection that can store many values of the same type in a sequence. An array has a specific type (e.g., int) and size. It cannot store values of different types (without converting them) or extend its size (without creating an entirely new array).

Declararing an array

The syntax to create an array is similar to a regular variable, with the addition of the desired size in square brackets.

So, for example, if you create an int variable like this:

int x;

You can create an int array like this:

int values[10];

This creates an array that can stores 10 ints. Note that we should not assume that the array is empty (all zeros), we might actually get random values inside of it. So it’s good practice to clear it up or store values in all positions before reading from it.

You can also initialize the values at declaration time:

int values[8]{1, 2, 4, 8, 16, 32, 64, 128};

If you’re using a “list-initializer” like the one above, you can ommit the size, as the compiler can infer from the values you want to store.

However, the size should be able to be identified at compilation time!
Something like this is not allowed by the compiler:

int n;
cin >> n;
int values[n];

If you compile using the flags we talked about here, you should get an error complaining about this declaration.

Just a quick mention, when we create arrays in this way, they are statically created in the stack. The stack is a place in memory reserved for our code to execute, and it has pre-defined limits. So you might end up going over the stack size if you try to create a huge array. We’ll discuss the stack in more detail later in the quarter!

Accessing values

To access the values of an array, you can use the square brackets [] and the position (i.e., index) you want to access. The first index is 0, and it grows until n-1, where n is your array size. In C++ arrays there are only positive indices, so you cannot use -1 to access the last element.

So to print an entire array, we could use:

int values[8]{1, 2, 4, 8, 16, 32, 64, 128, 256};

cout << values[0] << '\n';
cout << values[1] << '\n';
cout << values[2] << '\n';
cout << values[3] << '\n';
cout << values[4] << '\n';
cout << values[5] << '\n';
cout << values[6] << '\n';
cout << values[7] << '\n';

But this seems pretty long and repetitive… Luckily, we just saw how to use loops:

for (int i=0; i < 8; i++) {
  cout << values[i] << '\n';
}

Here we use a for-loop to iterate i through all indices of our values array, and this code would be equivalent to all those statements before.

Storing values

To store a value, you use similar syntax for access.
So you could do:

values[2] = 100;

If you want to update the 3rd position in our array.

If you use a different type, you might get a compiler warning or an implicit conversion.
For example, this:

values[4] = 3.14;

Might give you an error/warning like this:

test-array.cpp:38:15: error: implicit conversion from 'double' to 'int' changes value from 3.14 to 3 [-Werror,-Wliteral-conversion]
  values[4] = 3.14;
            ~ ^~~~
1 error generated.

Or simply truncate the value and store 3 in index 4.

Going out of bounds

What happens if you try to access an index that doesn’t exist? Well, it’s undefined behavior.

You might get an error (the famous segmentation fault), but your program might just read some unexpected value and think it’s working fine. So it’s important to do your best to not go over your array, as you might not even notice anything wrong.

Later in this course we’ll see some C++ classes that help with this, but for now you’ll want to ensure you stay inbounds!

Finding out the length of an array

Something that might help staying inbounds is to know how many elements are in your array. Although there is no direct way to find the length of an array, we can combine two values to do that. We’ll use sizeof, which we used to show the size of different variable types.

If you try to use that with an array, you might not get what you expect at first:

int x, y, values[]{1, 2, 4, 8, 16, 32, 64, 128, 256};
x = sizeof(values);
cout << "x = sizeof(values): " << x << '\n';

In my computer, this outputs 36. But that array clearly has fewer than 36 elements.

So what’s happening?

If you recall from the last time we used sizeof, it actually gives us the number of bytes that’s used. So in this case, we know that our array values uses 36 bytes.

We can combine that piece of data, with the size of a single element:

y = sizeof(values[0]);
cout << "y = sizeof(values[0]): " << y << '\n';

In my computer, this outputs 4. So now we know that a single int takes 4 bytes in my computer.

Finally, we can find the length of the array:

cout << "array length: x / y = " << x / y << '\n';

This should output 9, which matches the length of that array.

Important: this should work with local arrays, but once we start creating functions and having variables from other scopes, sizeof might work differentely! So it’s important to also know what’s the array size.

Multi-dimensional arrays

To store a two+-dimensional value, you don’t need a two+-dimensional array!

Assuming your 2D array is [N][M] sized, and you want to access [i][j], you could create a 1D array of size [N * M] and access [i * M + j].

For example, if you want a grid of 2x3, it is the same as a one-dimension array of 6 spots. Position [0][0] would be position [0], then, on the next row, position [1][1] would be position [4].

To make it more visual, this array:

0 1 2
3 4 5

Could be stored like this:

0 1 2 3 4 5

That being said, you can create a 2D array if you want. You just add all dimensions in the variable declaration:

int array[2][3];
array[1][2] = 5;

The same thing is true for other dimensions. If you have an array with size[M][N][O], you would access [i][j][k], but you can have an array of size [M * N * O] and access [i * (N * O) + j * O + k]. To create a 3D array:

int array[2][3][4];
array[1][2][3] = 6;

And so on. You can have as many dimensions as you want, you would just add more square brackets for sizes and indices. And the formula for a single array would have new pieces as you add more dimensions too.

Comparisons

Lastly, I wanted to briefly mention that we cannot compare arrays directly. So if you have two arrays that you want to check if they have the same elements, you’d have to loop over them and compare element by element.

References