Pointer arithmetic
Concepts
Section titled “Concepts”- What happens when you add a number to a pointer
- What subtracting two pointers yields
- UB when pointers do not point into the same memory block
- stack
Examples to aid understanding
Section titled “Examples to aid understanding”1. Moving a pointer by 1
Section titled “1. Moving a pointer by 1”int arr[2]{};int* p = arr;p = p + 1;*p = 2;
std::cout << arr[1];std::cout << std::endl;Answer
p + 1 advances the pointer by 1 element—that is, by 1 int, which is typically 4 bytes.
After this code executes, p equals &arr[1].
Reading from *p is equivalent to reading from arr[1].
2. Accessing an array element through an expression
Section titled “2. Accessing an array element through an expression”int arr[2]{};int* p = arr;*(p + 1) = 6;
std::cout << *p << std::endl;std::cout << arr[1] << std::endl;Answer
A pointer produced by an expression can be used just like one stored in an ordinary variable.
Note that *(p + 1) does not change p; it creates a temporary address offset from p.
*p reads 0 from the first array element.
3. Subtracting pointers
Section titled “3. Subtracting pointers”int arr[2]{};int* pStart = &arr[0];int* p = pStart + 1;
ptrdiff_t x = p - pStart;std::cout << x;std::cout << std::endl;
ptrdiff_t x1 = pStart - p;std::cout << x1;std::cout << std::endl;Answer
The - operator applied to two pointers gives the number of elements between them.
The result can also be negative.
The code will print 1 and then -1.
4. (advanced level): Subtracting addresses
Section titled “4. (advanced level): Subtracting addresses”#include <cstdint>#include <iostream>
int main(){ int arr[2]{}; int* a = &arr[0]; int* b = &arr[1]; uintptr_t a1 = reinterpret_cast<uintptr_t>(a); uintptr_t b1 = reinterpret_cast<uintptr_t>(b); ptrdiff_t x = static_cast<ptrdiff_t>(b1 - a1);
std::cout << x; std::cout << std::endl;}Answer
uintptr_t is an integer type with the same size as an int* (probably 8 bytes).
The operation b1 - a1 treats the addresses as numbers.
The expression no longer retains information about the original pointer types.
Instead, it operates on the pointers’ numeric values.
The result will be 4 — the difference between the addresses in bytes.
5. (advanced level): Going beyond a variable’s bounds
Section titled “5. (advanced level): Going beyond a variable’s bounds”int a = 5;int b = 6;int* pa = &a;int* pb = pa + 1;
std::cout << *pb;std::cout << std::endl;Answer
Technically, this is considered UB (undefined behavior).
It might print 6, the value of variable b.
6. (advanced level): Going beyond an array’s bounds
Section titled “6. (advanced level): Going beyond an array’s bounds”int a[2]{};int b = 6;int* p = &a[2];*p = 7;
std::cout << b;std::cout << std::endl;Answer
This is essentially the same situation as above. Remember that going beyond an array’s bounds can reach memory occupied by another variable. Going beyond an array’s bounds does not necessarily cause a runtime error in C++ and is not automatically prevented at runtime.
In this example, it might print 7.
7. Array assignment (1)
Section titled “7. Array assignment (1)”int arr1[2]{ 1, 2 };int arr2[2]{ 3, 4 };arr1 = arr2;Answer
Although it might seem that this should copy every element
from arr2 to arr1, the program will not compile.
This syntax simply does not work in C++.
8. Array assignment (2)
Section titled “8. Array assignment (2)”int arr1[2]{ 1, 2 };int arr2[2]{ 3, 4 };*arr1 = *arr2;Answer
In *arr1 = *arr2, the array names are used as expressions.
In this case, they are treated as pointers to their first elements.
Accordingly, this overwrites arr1[0] with arr2[0], that is, with 3.