Skip to main content

Section 13.3 Accessing Elements

When trying to visualize a vector, you should think of the name of the vector as a box containing a list of values. For example, you should imagine that the code vector<int> counts = {12, 6, 3, 14} creates something like this:
Figure 13.3.1. Memory diagram of vector counts containing 12, 6, 3, and 14.

Insight 13.3.1.

Just as with strings, because we index starting with 0, a vector with \(x\) elements has indices from 0 to \(x - 1\text{.}\)
The last element is always at index \(x - 1\text{.}\)
Sometimes, like when passing a vector to a function, we will want to work with the entire vector. But other times, what we want or need to work with are the individual elements of the vector. To do so we use the same syntax that we do with strings: the .at(index) function.
When used with a vector, the .at(index) function returns the element at the specified index. The type of that value always matches the type of the vectorโ€™s elements.
vector<int> counts = {10, 20, 30, 40};
int first = counts.at(0);    // first is 10

vector<string> words = {"apple", "banana", "cherry"};
string second = words.at(1); // second is "banana"

vector<double> measurements = {1.1, 2.2, 3.3};
double third = measurements.at(2); // third is 3.3
Also as with a string, we can assign to vec.at(index) to change a value in a vector. The value we assign to that location must be of the same type as the elements in the vector:
vector<char> symbols = {'x', 'q', '*', '!'};
symbols.at(2) = '$'; //{'x', 'q', '$', '!'}

vector<string> words = {"apple", "banana", "cherry"};
words.at(1) = "blueberry"; // {"apple", "blueberry", "cherry"}

Note 13.3.2.

Naming of elements can be tricky. The first element has the index 0. So some programmers refer to it as the โ€œzerothโ€ element. But that means โ€œfirstโ€ can refer to the element at index 0 or 1, depending on the convention being used.
For this reason, it is often best to explicitly refer to items by their index โ€œthe item at index 1โ€ is much clearer than โ€œfirstโ€.
In this book, if we use casual language (first, second, etc...) it will be to refer to the โ€œhuman numberingโ€ of elements where โ€œfirstโ€ means the first, not the item at index 1. When using C++ numbering (0 based), we will refer to โ€œthe element at index Xโ€ or โ€œelement index xโ€.
If you use .at(index) to ask for an index that is not valid (less than 0 or >= .size()), there will be a runtime error when that statement executes. This program runs fine until we try to access counts.at(4):
Listing 13.3.2.
As usual, the error message is a little cryptic. The important part is the out_of_range message. This means that the index you provided was not valid. After that, there is vector::_M_range_check: __n (which is 4) >= this->size() (which is 4). This tells us that the problem comes from a vector and we asked for index 4 in a vector with size of 4. Unfortunately, we do not get a line number, so we have to use print statements or a debugger to help track down the exact location.

Insight 13.3.3.

vec.at(index) = ... is only for changing existing values. You canโ€™t use it to add a value at an index that is not already in use.

Warning 13.3.4.

As with strings, vectors support bracket notation (vectorName[index]) notation for accessing elements. However, when using that syntax, there is no bounds checking. You can ask for element 100 in a 4 element vector and will be given some piece of memory that is unrelated to the vector (it will be whatever is in memory where element 100 would be if it existed).
To see this in action, change line 18 of Listingย 13.3.2 to say cout << counts[4] << endl;. Then run the program. There will not be an exception. Instead, some unpredictable value (possibly 0, possibly not) will be printed.
This kind of unchecked access is a common sources of bugs in code (do a web search for โ€œbuffer overflowโ€ for examples). The very minimal overhead required by the safe syntax (.at(index)) is worth it to avoid these kinds of bugs in the vast majority of situations.

Checkpoint 13.3.1.

Suppose the following code is run:
vector<string> message = {"happy", "to", "you", "September", "birthday", "girl"}
How would you save the string "birthday" from message to the variable word?
  • string word = message.at(4)
  • Vectors are zero-indexed, so the fifth element is index 4.
  • string word = message.at(5)
  • Remember, vectors are zero-indexed!
  • string word = message.at(6)
  • Remember, vectors are zero-indexed!
  • string word = message(4)
  • This is not proper vector indexing.
  • string word = message(5)
  • This is not proper vector indexing. Also, vectors are zero-indexed.

Checkpoint 13.3.2.

How could you increment the third element of vector<int> vec by one?
  • vec.at(3) = vec.at(3)++;
  • Incorrect! This is actually incrementing the 4th element of vec, since vectors are zero indexed.
  • vec(3) = vec(3) + 1;
  • Incorrect! This is not proper syntax. You need to use .at to access vector elements.
  • vec.at(2)++;
  • vec.at(2) is the third element and we increment it by using the ++ operator.
  • vec(2) = vec(2)++;
  • This is not proper syntax. You need to use .at to access vector elements.
  • vec.at(2) = vec.at(2) + 1
  • vec.at(2) is the third element and we increment it by adding 1.

Checkpoint 13.3.3.

Construct a block of code that changes the first element of vec to a 6, multiplies the third element of vec by 2, and increments the last element of vec by 1 (in that order). This should work no matter what vec is.
You have attempted of activities on this page.