Pointers, Arrays, and Pointer Arithmetic

The Array as a Memory Block

Memory Visualization

In C++, an array is stored as a contiguous block of memory. This means elements are placed right next to each other in sequence.

Think of memory as a series of numbered boxes. When you declare int arr[3], C++ reserves space for three integers, starting at a specific memory address.

Welcome back. To master pointers, we first need to visualize how arrays live in your computer's RAM. Imagine memory as a series of numbered boxes. When we declare an array of three integers, C++ reserves a contiguous block of space. If the first box is at address zero-x-one-zero-zero, the next must be at zero-x-one-zero-four because an integer typically takes up four bytes.

Memory as a Contiguous Block

The Mental Model

In C++, an array isn't just a collection; it's a contiguous block of memory. When you declare int arr[3], the compiler reserves three consecutive 'boxes' in RAM.

Because the memory is contiguous, the computer can calculate the location of any element if it knows the starting address and the size of the data type.

To master pointers, we must first visualize how arrays live in your computer's RAM. When we define an array of three integers, C++ grabs a solid chunk of memory. Notice how each index corresponds to a specific address, jumping by 4 bytes each time because that is the size of an integer. Each cell stores a value. By knowing the starting address, the CPU can instantly find any index using simple math.

The Secret of Array Decay

What is Array Decay?

In C++, the name of an array is not just a label—it acts as a pointer to its first element. This implicit conversion is called Array Decay.

int arr[3] = {10, 20, 30};
int* p = arr; // arr decays to &arr[0]

The most important rule to remember is Array Decay. In most contexts, the name of an array implicitly converts into a pointer to its first element. This means the variable 'arr' is essentially a synonym for the address of its first index. If you dereference it, you get the value stored right at the start.

The Rule of Array Decay

What's in a Name?

One of the most critical concepts in C++ is Array Decay. In most expressions, the name of the array implicitly converts into a pointer to its first element.

int* p = arr; // Decay happens here

This means arr and &arr[0] are effectively the same memory address.

You might wonder: what exactly is the variable 'arr' itself? In C++, the name of the array 'decays' into a pointer. It literally points to the very first box in that memory block we just saw. This means using 'arr' in code is usually just a shorthand for 'the address of the first element'.

Pointer Arithmetic & Scaling

The Scaling Factor

When you add 1 to a pointer, you aren't adding 1 byte. You are adding one unit of the data type. The compiler automatically scales the addition by the sizeof() the type.

Now, let's look at pointer arithmetic. When you add one to a pointer, you aren't moving forward by one single byte. Instead, the compiler uses a scaling factor. For an integer pointer, adding one moves the address forward by four bytes. But for a character pointer, it only moves by one byte. This ensures you always land exactly at the start of the next element. Notice how the integer pointer jumps from zero-x-one-zero-zero to zero-x-one-zero-four. The character pointer, however, only moves to zero-x-one-zero-one.

Pointer Arithmetic & Scaling

The Scaling Factor

When you add 1 to a pointer, the compiler doesn't just add 1 byte. It adds sizeof(T) bytes, where T is the data type.

Pointer arithmetic is smarter than basic math; it uses a scaling factor. If you increment an integer pointer, it leaps 4 bytes to reach the next integer. But a character pointer only moves 1 byte. This ensures that 'plus one' always means 'the next item', regardless of how big that item is.

Practice: Predict the Output

Code Challenge

Examine the code snippet. Based on the scaling rules we just discussed, what will the two print statements output?

Assume numbers starts at address 0x100.

Time for a challenge. Look at this snippet where we have both an integer pointer and a character pointer looking at the same array. Based on what you've learned about scaling, what will these lines print? Type your predictions and submit.

Predict the Jump

Look at the code and predict the output. How does the data type change the result of pointer arithmetic?

It's time to test your intuition. Look at this snippet. We have an array of integers, but we're looking at it through two different lenses: an integer pointer and a character pointer. Type in what you think the printed values will be.

Iterating with Pointers

The Pointer Loop

We can navigate an array without using indices by using start and end pointers. This is a common pattern for high-performance C++ code.

int* ptr = arr;
int* end = arr + 3;
while(ptr < end) {
  // do work
  ptr++;
}

Instead of using an index like 'i', we can use pointers to walk through memory directly. We set one pointer to the start and another just past the end as a 'sentinel'. As we increment the pointer, it steps through the array until it hits the sentinel, providing a clean and efficient way to process data.

Iterating with Pointers

The Pointer Loop Pattern

Instead of using an index i, low-level C++ often uses pointers to traverse data. We use a start pointer and a sentinel (end) pointer.

  1. ptr starts at arr.
  2. end points just past the last element.
  3. Loop while ptr < end.

In professional systems programming, we often iterate using only pointers. We set a pointer to the start of the array and a sentinel pointer to the end. As we increment the pointer, it 'walks' through memory. The loop continues as long as our current address is less than the end address.

The Socratic Pointer Tutor

Ask the tutor about pointer arithmetic or array decay to clarify your understanding.

Pointers are notoriously tricky. If you're still feeling a bit hazy about how adding 1 to a pointer works, or why arrays decay, ask me a question. I'll help guide you to the answer.

Common Pitfalls

Danger Zones

Pointers are powerful but offer no safety rails. Two major traps await the unwary:

Before we wrap up, a word of caution. Pointers give you direct access to memory, but they don't come with safety rails. If you increment a pointer too far, you'll access memory you don't own, leading to crashes. Also, remember that once an array decays, it loses its size information. To the compiler, it's just a lonely address.