Content
Introduction of Array Arrays and Pointers Pointer Arithmetic Array Indexing Pointer Comparison Traversal by Pointer Array Parameters and Functions <- Go BackUniversity of Michigan at Ann Arbor
Last Edit Date: 01/28/2023
Disclaimer and Term of Use:
We do not guarantee the accuracy and completeness of the summary content. Some of the course material may not be included, and some of the content in the summary may not be correct. You should use this file properly and legally. We are not responsible for any results from using this file
This personal note is adapted from Professor Amir Kamil, Andrew DeOrio, James Juett, Sofia Saleem, and Saquib Razak. Please contact us to delete this file if you think your rights have been violated.
This work is licensed under a Creative Commons Attribution 4.0 International License.
Note that there are several different categories of objects in C++:
Atomic types are built into the language. Also known as primitive types. Types such as int
, double
, bool
, char
, int *
, string *
, and so on are all atomic types.
Arrays are contiguous sequences of objects of the same type (homogeneous).
Class-type objects are objects composed of member subobjects, each which may be of a differnt type (heterogeneous).
Properties of arrays:
An array has a fixed size.
The size of an array cannot be changed as long as the array is alive.
An array holds elements that are of the same type.
The elements of an array are stored in a specific order, with the index fo the first element being 0.
The elements are stored contiguously in memory, one after another.
Accessing any element of an array takes constant time, regardless of whether the element is at the beginning, middle, or end of the array.
1 int main(){ 2 int array[4] = {1, 2, 3, 4}; // 1, 2, 3, 4 3 int array1[4] = {1, 2}; // 1, 2, 0, 0 4 int array3[4] = {}; // 0, 0, 0, 0 3 }
Try it out
Click here to open in new windowThe following picture shows how the array in line 2 is stored in the memory.
Note that individual array elements can be accessed with square brackets, with an index between the brackets. Indexing starts at 0, up through the size of the array minus one.
The following increments each element in array by one and prints out each resulting value:
1 int main(){ 2 int array[4] = {1, 2, 3, 4}; 3 for (int i = 0; i < 4; ++i) { 4 ++array[i]; 5 cout << array[i] << endl; 6 } 7 }
Try it out
Click here to open in new windowWhen we use an array in a context where a value is required, the compiler converts the array into a pointer to the first element in the array.
1 int array[] = { 1, 2, 3, 4 }; 2 cout << &array[0] << endl; // prints the address of the first element 3 cout << array << endl; // prints the address of the first element 4 *array = -1; 5 cout << array[0] << endl; // prints -1
Try it out
Click here to open in new windowThe tendency of arrays to decay into pointers results in significant limitations when using an array.
For instance, we cannot assign one array to another – the right-hand side of an assignment requires a value, which in the case of an array will become a pointer, which is then incompatible with the left-hand side array:
1 int arr1[4] = { 1, 2, 3, 4 }; 2 int arr2[4] = { 5, 6, 7, 8 }; 3 arr2 = arr1; // error: LHS is an array, RHS is a pointer
By default, C++ passes parameters by value. This is also true if the parameter is an array. Since an array decays to a pointer when its value is required, this implies that an array is passed by value as a pointer to its first element.
Thus, an array parameter to a function is actually equivalent to a pointer parameter, regardless of whether or not the parameter includes a size.
This means that a function that takes an array as a parameter cannot guarantee that the argument value corresponds to an array of matching size, or even that it is a pointer into an array.
Instead, we need another mechanism for passing size information to a function.
There are some certain arithmetic operations on pointers:
An integer can be added to or subtracted from a pointer, resulting in a pointer that is offset from the original one.
Two pointers can be subtracted, resulting in an integer that is the distance between the pointers.
Pointer arithmetic is in terms of number of elements rather than number of bytes.
For instance, if an int
takes up four bytes of memory, then adding 2 to an int *
results in a pointer that is two int
s forward in memory, or a total of eight bytes.
1 int array[] = { 4, 3, 2, 1 }; 2 int *ptr1 = array; // pointer to first element 3 int *ptr2 = &array[2]; // pointer to third element 4 int *ptr3 = ptr1 + 2; // pointer to third element 5 int *ptr4 = array + 2; // pointer to third element 6 ++ptr1; // move pointer to second element
Try it out
Click here to open in new windowWe can get the distance of two pointers by subtracting them.
1 int arr[5] = {6, 3, 2, 4, 5}; 2 int *a = arr; 3 int *b = arr + 2; 4 int *c = b + 1; 5 int *d = &arr[1]; 6 7 ++a; 8 --b; 9 c = d; 10 c += 2; 11 12 cout << *a << endl; 13 cout << *(a + 2) << endl; 14 cout << (a - d) << endl; 15 cout << (b - c) << endl; 16 cout << b[2] << endl; 17 cout << *(arr + 1) << endl; 18 cout << *(arr + 5) << endl;
Try it out
Click here to open in new windowWe can see that the last line generate an error. That is because it is out of the scope of the array.
Array indexing in C++ is actually implemented using pointer arithmetic. If one of the operands to the subscript ([]
) operator is an array and the other is integral, then the operation is equivalent to pointer arithmetic followed by a dereference.
1 int arr[4] = { 1, 2, 3, 4 }; 2 cout << *(arr + 2) << endl; // prints 3: arr+2 is pointer to 3rd element 3 cout << arr[2] << endl; // prints 3: equivalent to *(arr + 2) 4 cout << 2[arr] << endl; // prints 3: equivalent to *(2 + arr);
Try it out
Click here to open in new windowWe can compare pointers' addresses and the values they point to using operatoes <
, >
, <=
, >=
, ==
, and !=
.
1 int arr[5] = { 5, 4, 3, 2, 1 }; 2 int *ptr1 = arr + 2; 3 int *ptr2 = arr + 3; 4 bool comparison1 = ptr1 == ptr2; 5 cout << comparison1 << endl; // 0 6 bool comparison2 = ptr1 == ptr2 - 1; 7 cout << comparison2 << endl; // 1 8 bool comparison3 = ptr1 < ptr2; 9 cout << comparison3 << endl; // 1 10 bool comparison4 = *ptr1 < *ptr2; 11 cout << comparison4 << endl; // 0 12 bool comparison5 = ptr1 < arr + 5; 13 cout << comparison5 << endl; // 1
Try it out
Click here to open in new windowTraversal by Index: Start an index variable (e.g. i
) at 0
, increase it by 1 on each iteration of the loop, and plug i
into an indexing operation to find each element of the array.
1 int arr[5] = { 5, 4, 3, 2, 1 }; 2 for (int i = 0; i < 5; i++) { 3 cout << arr[i] << endl; 4 }
Try it out
Click here to open in new windowTraversal by Pointer: Start a pointer (e.g. ptr
) at the beginning of an array, move it forward one space in memory on each iteration, and dereference it along the way to visit each element of the array.
1 int arr[5] = { 5, 4, 3, 2, 1 }; 2 for (int *ptr = arr; ptr < arr + 5; ptr++) { 3 cout << *ptr << endl; 4 }
Try it out
Click here to open in new windowThe compiler turns array parameters into pass-by-pointer behind the scenes. That gives us a pointer we can use to access the original array. This is similar to pass-by-reference, but technically different.
Because of this, the only thing passed into an array function is a pointer to the first element. That means we have to pass the size of the original array as a separate parameter.
Here is an example function called maxValue
that uses traversal-by-pointer to find the value of the maximum element in an array.
Try it out
Click here to open in new window