Loading...

Relationship Between Arrays and Pointers

Array-to-pointer decay, contiguous layout, and safe traversal via pointer arithmetic.

In C++, arrays and pointers are closely related. This is because arrays are stored sequentially in memory, and the name of an array actually represents the address of its first element. Therefore, it is possible to traverse an array using pointer arithmetic. In this article, we will learn about the memory structure of arrays, their relationship with pointers, and practical usage examples.


1. Arrays and Memory Layout

When an array is defined in C++, all its elements are stored consecutively in memory. For example:


#include <iostream>
using namespace std;

int main() {
    int numbers[3] = {10, 20, 30};

    cout << "Address of 1st element: " << &numbers[0] << endl;
    cout << "Address of 2nd element: " << &numbers[1] << endl;
    cout << "Address of 3rd element: " << &numbers[2] << endl;

    return 0;
}

The output addresses will appear sequentially:


Address of 1st element: 0x61ff0c  
Address of 2nd element: 0x61ff10  
Address of 3rd element: 0x61ff14

Since each int occupies 4 bytes, the addresses increase by 4.


2. The Array Name Is a Pointer

The name of the array (numbers) actually represents the address of its first element. In other words, numbers and &numbers[0] refer to the same address.


int numbers[3] = {10, 20, 30};
cout << numbers << endl;      // address of first element
cout << &numbers[0] << endl; // same address

Therefore, you can traverse an array using pointers.


3. Accessing Array Elements with Pointers

If a pointer points to the first element of an array, pointer arithmetic can be used to access the other elements.


#include <iostream>
using namespace std;

int main() {
    int numbers[4] = {5, 10, 15, 20};
    int *p = numbers; // p = &numbers[0]

    cout << *p << endl;      // 5
    cout << *(p + 1) << endl; // 10
    cout << *(p + 2) << endl; // 15
    cout << *(p + 3) << endl; // 20
}

The expression *(p + i) is equivalent to numbers[i]. This forms the foundation of pointer arithmetic.


4. Pointer Arithmetic

Pointers automatically move by the correct amount in memory based on their type. For example:


int array[3] = {1, 2, 3};
int *ptr = array;

cout << ptr << endl;       // address of first element
ptr++;
cout << ptr << endl;       // address of the next element

The expression ptr++ actually advances the address by 4 bytes (int = 4 bytes). This is automatically calculated based on the pointer’s type. For example, double advances by 8 bytes, while char advances by 1 byte.


5. Iterating Through an Array Using a Pointer

You can loop through an array using a pointer.


int numbers[] = {2, 4, 6, 8, 10};
int *p = numbers;

for (int i = 0; i < 5; i++) {
    cout << *(p + i) << " ";
}

Alternatively, the pointer can be incremented directly:


for (int *ptr = numbers; ptr < numbers + 5; ptr++) {
    cout << *ptr << " ";
}

Both methods produce the same result:


2 4 6 8 10

6. Array Parameters in Functions

When an array is passed to a function, what is actually passed is the address of the array. Therefore, the function can modify the array directly.


#include <iostream>
using namespace std;

void Fill(int *array, int length) {
    for (int i = 0; i < length; i++)
        array[i] = (i + 1) * 5;
}

void Print(const int *array, int length) {
    for (int i = 0; i < length; i++)
        cout << array[i] << " ";
}

int main() {
    int numbers[5];
    Fill(numbers, 5);
    Print(numbers, 5);
}

Since the Fill function modifies the array directly in memory, the values in the main() function are updated.


7. Multidimensional Arrays and Pointers

Multidimensional arrays are also stored sequentially in memory. However, pointer arithmetic requires a bit more care.


#include <iostream>
using namespace std;

int main() {
    int matrix[2][3] = {
        {1, 2, 3},
        {4, 5, 6}
    };

    cout << *(*(matrix + 0) + 2) << endl; // 3
    cout << *(*(matrix + 1) + 0) << endl; // 4
}

Here, matrix → is the address of the first row. *(matrix + 1) → moves to the second row, and *(*(matrix + 1) + 0) → accesses the first element of that row.


8. Pointers and Dynamic Arrays

Unlike fixed-size arrays, dynamic arrays are created using the new keyword. These arrays are stored in heap memory, and their size can be determined at runtime.


int *array = new int[5];

for (int i = 0; i < 5; i++)
    array[i] = (i + 1) * 10;

for (int i = 0; i < 5; i++)
    cout << array[i] << " ";

delete[] array; // Free memory

Dynamic arrays require manual memory management. Memory allocated with new must always be released with delete[] after use.


9. Reading Arrays with const Pointers

If you don't want a function to modify the array, the pointer parameter can be defined as const.


void Print(const int *array, int length) {
    for (int i = 0; i < length; i++)
        cout << array[i] << " ";
}

This approach preserves data integrity and prevents the array from being accidentally modified.


10. TL;DR

  • The array name (numbers) → is the address of the first element (&numbers[0]).
  • *(p + i)array[i] are equivalent expressions.
  • Pointer arithmetic automatically advances the address based on the data type size.
  • Arrays are passed to functions by address, not by value (no copying).
  • Multidimensional arrays can be accessed through pointer chains (*(*(matrix + i) + j)).
  • Dynamic arrays should be created with new and freed with delete[].
  • All examples can be run in Visual Studio 2022 or GCC 11+ compilers.

Related Articles