Search

6.8a — Pointer arithmetic and array indexing

Pointer arithmetic

The C++ language allows you to perform integer addition or subtraction operations on pointers. If ptr points to an integer, ptr + 1 is the address of the next integer in memory after ptr. ptr - 1 is the address of the previous integer before ptr.

Note that ptr + 1 does not return the memory address after ptr, but the memory address of the next object of the type that ptr points to. If ptr points to an integer (assuming 4 bytes), ptr + 3 means 3 integers (12 bytes) after ptr. If ptr points to a char, which is always 1 byte, ptr + 3 means 3 chars (3 bytes) after ptr.

When calculating the result of a pointer arithmetic expression, the compiler always multiplies the integer operand by the size of the object being pointed to. This is called scaling.

Consider the following program:

On the author’s machine, this output:

0012FF7C
0012FF80
0012FF84
0012FF88

As you can see, each of these addresses differs by 4 (7C + 4 = 80 in hexadecimal). This is because an integer is 4 bytes on the author’s machine.

The same program using short instead of int:

On the author’s machine, this output:

0012FF7C
0012FF7E
0012FF80
0012FF82

Because a short is 2 bytes, each address differs by 2.

Arrays are laid out sequentially in memory

By using the address-of operator (&), we can determine that arrays are laid out sequentially in memory. That is, elements 0, 1, 2, … are all adjacent to each other, in order.

On the author’s machine, this printed:

Element 0 is at address: 0041FE9C
Element 1 is at address: 0041FEA0
Element 2 is at address: 0041FEA4
Element 3 is at address: 0041FEA8

Note that each of these memory addresses is 4 bytes apart, which is the size of an integer on the author’s machine.

Pointer arithmetic, arrays, and the magic behind indexing

In the section above, you learned that arrays are laid out in memory sequentially.

In lesson 6.8 -- Pointers and arrays, you learned that a fixed array can decay into a pointer that points to the first element (element 0) of the array.

Also in a section above, you learned that adding 1 to a pointer returns the memory address of the next object of that type in memory.

Therefore, we might conclude that adding 1 to an array should point to the second element (element 1) of the array. We can verify experimentally that this is true:

Note that when dereferencing the result of pointer arithmetic, parenthesis are necessary to ensure the operator precedence is correct, since operator * has higher precedence than operator +.

On the author’s machine, this printed:

0017FB80
0017FB80
7
7

It turns out that when the compiler sees the subscript operator ([]), it actually translates that into a pointer addition and dereference! Generalizing, array[n] is the same as *(array + n), where n is an integer. The subscript operator [] is there both to look nice and for ease of use (so you don’t have to remember the parenthesis).

Using a pointer to iterate through an array

We can use a pointer and pointer arithmetic to loop through an array. Although not commonly done this way (using subscripts is generally easier to read and less error prone), the following example goes to show it is possible:

How does it work? This program uses a pointer to step through each of the elements in an array. Remember that arrays decay to pointers to the first element of the array. So by assigning ptr to name, ptr will also point to the first element of the array. Each element is dereferenced by the switch expression, and if the element is a vowel, numVowels is incremented. Then the for loop uses the ++ operator to advance the pointer to the next character in the array. The for loop terminates when all characters have been examined.

The above program produces the result:

Mollie has 3 vowels

Because counting elements is common, the <algorithm> library offers std::count_if, which counts elements that fulfill a condition. We can replace the for-loop with a call to std::count_if.

std::begin and std::end only work on arrays with a known size. If the array decayed to a pointer, we can calculate begin and end manually.

6.8b -- C-style string symbolic constants
Index
6.8 -- Pointers and arrays

177 comments to 6.8a — Pointer arithmetic and array indexing

  • elvis

    in the example where it sorts through mollie to find how many vowels there are. the for loop has this condition in it ptr < (name + arrayLength) how does it add name and int? I tried printing "name + arrayLength" and i get random garbage. does name and array length convert to pointers when they are being compared to ptr?

    • nascardriver

      Hi Elvis!

      Your question is the main topic of this lesson, I suggest you re-read it.
      `name + arrayLength` returns a pointer that points `arrayLength` bytes after `name`.

  • Ged

    Code is missing the <algorithm> library. And why are we using std::size_t, because when I try to run it I get an error and if I change it to an int, it works. Only if I use the static_cast<std::size_t>( ) it works, but why do the extra work? As I understand size_t is an unsigned int which you told to avoid if you can.

    • nascardriver

      > Code is missing the library
      Added.

      > why are we using std::size_t
      I thought `std::count_if` returned an `std::size_t`. It returns a `std::ptrdiff_t` in this case. Code updated to use `auto`.

      > As I understand size_t is an unsigned int which you told to avoid if you can.
      It's an unsigned integer, but not necessarily an unsigned int. If you don't modify an unsigned integer and don't use it for arithmetic, there's nothing that can go wrong. If `std::count_if` returned a `std::size_t`, we would've needed an extra cast when all we want to do is print the result.

      Thanks!

  • Ged

    It turns out that when the compiler sees the subscript operator ([]), it actually translates that into a pointer addition and dereference! Generalizing, array[n] is the same as *(array + n), where n is an integer. The subscript operator [] is there both to look nice and for ease of use (so you don’t have to remember the parenthesis).

    Isn't [] used to save a changed value which *() can't do?

  • Jack Overby

    I'm trying to loop through and perform a regex check on each character, rather than switch-10 different cases:

    However, I keep getting the following compiler error message:

    no instance of overloaded function "std::regex_match" matches the argument list

    Any suggestions?

    • nascardriver

      Regex shouldn't be used for this. It has a huge overhead compared to manually solving the task.
      You'll get the best results from using a `switch`. I understand you don't want to do that.
      You can use an `std::set` and `std::count_if` instead.

      If you really wanted to use regex, which you shouldn't, you could use an `std::sregex_iterator` and `std::distance`. `std::sregex_iterator` iterates over all found matches.

  • alfonso

    Here the char array name does not decay or std::cout treats it in a special way.

    And maybe for the same reason, the following code gives me strange results:

Leave a Comment

Put all code inside code tags: [code]your code here[/code]