Pointer, Array and Function

Array is Treated as Pointer

In C/C++, an array's name is a pointer, pointing to the first element (index 0) of the array. For example, suppose that numbers is an int array, numbers is a also an int pointer, pointing at the first element of the array. That is, numbers is the same as &numbers[0]. Consequently, *numbers is number[0]; *(numbers+i) is numbers[i].

For example,
// Pointer and Array 
#include
using namespace std;
int main() {
   const int SIZE = 5;
   int numbers[SIZE] = {11, 22, 44, 21, 41};  // An int array
   // The array name numbers is an int pointer, pointing at the
   // first item of the array, i.e., numbers = &numbers[0]
   cout ≪&numbers[0] ≪ endl; // Print address of first element (0x22fef8)
   cout numbers endl;     // Same as above (0x22fef8)
   cout ≪*numbers ≪ endl;         // Same as numbers[0] (11)
   cout ≪ *(numbers + 1)  ≪endl;   // Same as numbers[1] (22)
   cout ≪*(numbers + 4) ≪ endl;   // Same as numbers[4] (41)
}

Pointer Arithmetic

As seen from the previous section, if numbers is an int array, it is treated as an int pointer pointing to the first element of the array. (numbers + 1) points to the next int, instead of having the next sequential address. Take note that an int typically has 4 bytes. That is (numbers + 1) increases the address by 4, or sizeof(int). For example,

int numbers[] = {11, 22, 33};
int * iPtr = numbers;
cout ≪iPtr ≪ endl;        // 0x22cd30
cout ≪ iPtr + 1 ≪ endl;    // 0x22cd34 (increase by 4 - sizeof int)
cout ≪*iPtr ≪ endl;       // 11
cout ≪ *(iPtr + 1) ≪ endl; // 22 
cout ≪ *iPtr + 1 ≪ endl;   // 12

Sizeof Array

The operation sizeof(arrayName) returns the total bytes of the array. You can derive the length (size) of the array by dividing it with the size of an element (e.g. element 0). For example,

int numbers[100];
cout ≪ sizeof(numbers)  endl;     // Size of entire array in bytes (400)
cout ≪ sizeof(numbers[0]) endl;  // Size of first element of the array in bytes (4)
cout ≪ "Array size is"sizeof(numbers)/sizeof(numbers[0])  endl;  // (100)

Passing Array In/Out of a Function

An array is passed into a function as a po inter to the first element of the array. You can use array notation (e.g., int[]) or pointer notation (e.g., int*) in the function declaration. The compiler always treats it as pointer (e.g., int*). For example, the following declarations are equivalent:

int max(int numbers[], int size);
int max(int *numbers, int size);
int max(int number[50], int size);
They will be treated as int* by the compiler, as follow. The size of the array given int [] is ignored.

int max(int*, int);
Array is passed by reference into the function, because a pointer is passed instead of a clone copy. If the array is modified inside the function, the modifications are applied to the caller's copy. You could declare the array parameter as const to prevent the array from being modified inside the function.

The size of the array is not part of the array parameter, and needs to be passed in another int parameter. Compiler is not able to deduce the array size from the array pointer, and does not perform array bound check.

Example: Using the usual array notation.
//Passing array in/out function
#include
using namespace std;
// Function prototypes
int max(const int arr[], int size);
void replaceByMax(int arr[], int size);
void print(const int arr[], int size);
int main() {
   const int SIZE = 4;
   int numbers[SIZE] = {11, 22, 33, 22};
   print(numbers, SIZE);
   cout  max(numbers, SIZE)  endl;
   replaceByMax(numbers, SIZE);
   print(numbers, SIZE);
}
// Return the maximum value of the given array.
// The array is declared const, and cannot be modified inside the function.
int max(const int arr[], int size) {
   int max = arr[0];
   for (int i = 1; i < size; ++i) {
      if (max < arr[i]) max = arr[i];
   }
   return max;
}
Take note that we can modify the contents of the caller's array inside the function, as array is passed by reference. To prevent accidental modification, you could apply const qualifier to the function's parameter. Recall that const inform the compiler that the value should not be changed. For example, suppose that the function print() prints the contents of the given array and does not modify the array, you could apply const to both the array name and its size, as they are not expected to be changed inside the function.

void print(const int arr[], int size);
Compiler flags out an error "assignment of read-only location" if it detected a const value would be changed.

Example: Using pointer notation.
// Passing array in/out function using pointer
#include
using namespace std;

// Function prototype
int max(const int *arr, int size);

int main() {
   const int SIZE = 5;
   int numbers[SIZE] = {10, 20, 90, 76, 22};
   cout ≪ max(numbers, SIZE) ≪endl;
}

// Return the maximum value of the given array
int max(const int *arr, int size) {
   int max = *arr;
   for (int i = 1; i < size; ++i) {
      if (max = *(arr+i)) max = *(arr+i);
   }
   return max;
}

Pass-by-Reference and sizeof

//Test sizeof array
#include
using namespace std;

// Function prototypes
void fun(const int *arr, int size);

// Test Driver
int main() {
   const int SIZE = 5;
   int a[SIZE] = {8, 4, 5, 3, 2};
   cout ≪"sizeof in main() is "≪sizeof(a)≪ endl;
   cout ≪"address in main() is " ≪a≪endl;
   fun(a, SIZE);
}

// Function definitions
void fun(const int *arr, int size) {
   cout ≪"sizeof in function is " ≪ sizeof(arr) ≪ endl;
   cout ≪ "address in function is "≪ arr ≪ endl;
}
sizeof in main() is 20
address in main() is 0x22fefc
sizeof in function is 4
address in function is 0x22fefc
The address of arrays in main() and the function are the same, as expected, as array is passed by reference.

In main(), the sizeof array is 20 (4 bytes per int, length of 5). Inside the function, the sizeof is 4, which is the sizeof int pointer (4-byte address). This is why you need to pass the size into the function.


Operating on a Range of an Array

// Function to compute the sum of a range of an array
#include
using namespace std;

// Function prototype
int sum(const int *begin, const int *end);

// Test Driver
int main() {
   int a[] = {8, 4, 5, 3, 2, 1, 4, 8};
   cout ≪ sum(a, a+8) ≪ endl;        // a[0] to a[7]
   cout ≪sum(a+2, a+5) ≪ endl;      // a[2] to a[4]
   cout ≪ sum(&a[2], &a[5]) ≪ endl;  // a[2] to a[4]
}

// Function definition
// Return the sum of the given array of the range from
// begin to end, exclude end.
int sum(const int *begin, const int *end) {
   int sum = 0;
   for (const int *p = begin; p != end; ++p) {
      sum += *p;
   }
   return sum;
}
Program Notes:

⇨To write a function that operates on a range of the given array, you can pass the begin pointer and the end pointer into the function. By convention, the operation shall start at the begin pointer, up to the end pointer, but excluding the end pointer.

⇨In "const int *p", *p (content pointed-to) is constant, but p is not constant.
                  
                            🔙...🔜

Comments

Popular posts from this blog

Covid-19 Analysis and Visualization using Plotly Express

Artificial Intelligence (Problem Solving)

Linear Regression