Pointer and Array in C++

Introduction

This blog attempts to clarify the concepts and syntaxes involving the usage of pointer and array. It explains:

  • Concept and syntax differences between pointer to element at the start of the array and pointer to the whole array.
  • A phenomenon called "Array decay" in C++ when the size and type of array is loss when passed into a function.

Pointer to base address of array

Before going further, I suggest this website as starting reference [1]. It shows some important concepts and syntaxes regarding pointer and array:

  • Pointer (to base address of an array) can be used to access element of the array.
  • Array name can also be used as pointer.

For example:

int arr[5] = {1,2,3,4,5};
// then the following are the same: 
cout <<arr[1]<<endl;       // arr element is accessed using array index operator.
cout <<*(arr+1)<<endl;     // arr is used like pointer to access array element.

In fact, array and pointer are closely related.

int arr[5] = {1,2,3,4,5};
int *ptr   = arr; 
int *ptr1  = &arr[0];

In the above codes, both ptr and ptr1 are the same.

Let go further to differentiate pointer to the base address of array vs pointer to the whole array.

Pointer to the 1st array element vs Pointer to the whole array

Pointer to whole arrayPointer to base address of the array
Syntax<type> (*ptr_name) [] = &array<type> *ptr = array
Content stores the address of the first element of the array. also stores the address of the first element of the array
Sizethe size of the whole arraythe size of an element in the array
Arithmetic operatormove the pointer to a different memory location, offset by the size of the array.move the pointer to the next element. Pointer behaves as iterator.

The following program demonstrates the above differences:

#include <iostream>
using namespace std;

int main()
{
    // Pointer to an integer
    int *p; 
    
    // Pointer to an array of 5 integers
    int (*ptr)[5]; 

    int arr[5] = {1,2,3,4,5};
     
    // Points to 0th element of the arr.
    p = arr;
     
    // Points to the whole array arr.
    ptr = &arr; 

    cout<<"sizeof(*p)   "<<sizeof(*p)<<endl;      // print 4 bytes
    cout<<"sizeof(*ptr) "<< sizeof(*ptr)<<endl;   // print 20 bytes
    
    // both pointers stores the base address of the array.
    cout << "p =" << p <<", ptr = "<< ptr<< endl;  

    p++; 
    ptr++;
    // ptr is incremented by 20 bytes, while p is increment by only 4 bytes:
    cout << "p =" << p <<", ptr = "<< ptr<< endl;   

    // access the value of the element in the array by dereference pointer
    cout << "arr[2] = " << *p << endl;
    cout << "arr[3] = " << *++p << endl;
    cout << "arr[4] = " << *++p << endl; 

    return 0;
}

Array decay

Array decay in C++ refers to the loss of type and dimensions of an array.

It happens when we pass the array into function by value. It doesn't happen if we pass the array into function by pointer (to the whole array), or by reference.

  • pass by value is done by passing the actual array or pointer to the base address of the array to the function..
  • pass by pointer is done by passing the pointer to the whole array to the function.
  • pass by reference is done by using reference syntax and pass an alias of the array to the function.

The following program illustrates 3 ways that an array could be passed into a function.

#include <iostream>
using namespace std;

void by_value( int* array){
    cout<<"size pass by by_value "<<sizeof(*array)<<endl;
};

void by_value_arr( int arr [5]){
    cout<<"size pass by by_value "<<sizeof(*arr)<<endl;
};

void by_pointer( int (*array)[5]){
    cout<<"size pass by pointer  "<<sizeof(*array)<<endl;
};

void by_reference( int (&array)[5]){
    cout<<"size pass by reference "<<sizeof(array)<<endl;
};

int main()
{
    // Pointer to an integer
    int *p; 
    
    // Pointer to an array of 5 integers
    int (*ptr)[5]; 

    int arr[5] = {1,2,3,4,5};
     
    // Points to 0th element of the arr.
    p = arr;
     
    // Points to the whole array arr.
    ptr = &arr; 

    // size of actual array
    cout<<"sizeof(array)  "<< sizeof(arr)<<endl;
    cout<<"sizeof(*ptr)   "<< sizeof(*ptr)<<endl;

    by_value(p);         // print incorrect size=4 bytes
    by_value_arr(arr);   // print incorrect size=4 bytes
    by_pointer(ptr);     // print correct size=20 bytes
    by_reference(arr);   // print correct size=20 bytes
     
    return 0;
}
  • Note that we print the size of the array via pointer by sizeof(*ptr) not by sizeof(ptr) !!!
  • Note that in function by_value_arr( int arr [5]), we want print the size of the array by sizeof(*arr) not sizeof(arr). The reason is that sizeof(arr) would be size of the pointer to the first element of arr. However, as array decay issue kicks in, we only have the size of the first element which is 4 bytes.

Output:

sizeof(array)  20
sizeof(*p)   4
sizeof(*ptr)   20
size pass by by_value 4
size pass by by_value 4
size pass by pointer  20
size pass by reference 20

Why do arrays decay to pointer?

In the above section, we note the syntax that passes array by value to function as below:

void by_value( int * ptr);
void by_value_arr( int arr [5]);    // must specify the size if we want to get size by sizeof()

int *ptr is "the same" as int arr[5]. Using ptr (pointer to base address of the array) to pass the array was more convenient as no size is needed. However, it leads to Array Decay issue.

Also note that we could also dynamically allocate an array in the heap using pointer. It is the same concept, as show below:

int* p = new int[5] {10,20,30,40, 50};
// thus we could use p[i] to access element of the array.
for (int i = 0; i < 5; i++) {
    cout<<p[i]<<endl;
}

Thus we could sum up the following concept:

Array and pointers are closely related to each other. In C++, the name of an array is considered as a pointer, i.e., the name of an array contains the address of an element Ref [2]

Why it happens is discussed here: Ref [3]

Basically C inherits the legacy of earlier programming languages: arrays were chunks of memory blocks, and its elements were accessible by pointer.

Anyway, when passing the array to a function, we should use something like below:

void by_pointer( int (*array)[5])   // must specify size to be able to use sizeof
void by_value_arr( int arr [5])     // must specify size to be able to use sizeof

Or we could have a struct whose member is an array.

Reference:

[1] C++ Pointers and Arrays (programiz.com)

[2] C++ Array of Pointers - javatpoint

[3] Why do arrays in C decay to pointers? - Stack Overflow

Leave a Reply

Your email address will not be published. Required fields are marked *