Individual elements are identified by address. Unlike ordinary variables, we can't identify them by name because array elements are anonymous; they have no name. However, we always know the start address of the array (which can be named) and every element in the array can be referred to by its zero-based offset from this address. That is, the first element is offset 0 elements from the start address because there are zero elements before it. By the same token the nth element is offset n-1 elements from the start address because there are n-1 elements in front of it.
int a[100];
int* p1 = a + 0; // refers to the 1st element (1-1=0)
int* p2 = a + 5; // refers to the 6th element (6-1=5)
int* p3 = a + 99; // refers to the 100th element (100-1=99), the last element
int* p4 = a + 100; // refers to one-past-the-end of the array
Note that p4 is valid because that address is guaranteed to exist even though it is physically beyond the bounds of the array. Although valid, we must never dereference that address because it is not guaranteed to belong to our program. However, we often use the one-past-the-end of a sequence in algorithms that use the end iterator to denote the end of a half-closed range denoted [begin:end). For instance, the following algorithm provides the most efficient means of linearly searching for a given value within any given contiguous sequence of an array, returning the end iterator if the value does not exist:
int* find (int* begin, int* end, int value) {
// search every element to find the value, unless we reach the end iterator
while (begin!=end && *begin!=value) ++begin;
return begin;
}
// search for the value 42 within the whole array
int* i1 = find (a, a+100, 42);
if (i1 == a+100) {
// the value was not found
} else {
// the value was found
}
int*_t i2 = find (a, a+10, 42); // search for the value 42 within the first 10 elements only...
They array suffix operator [] provides a more intuitive means of referring to the individual elements of an array without resorting to pointer arithmetic:
// initialise the array referred to by a of length s elements with the value v
void initialise_all (int* a, size_t s, int v)
{
for (size_t idx=0; idx<size; ++idx) // idx is the zero-based index
{
a[idx] = value;
}
}
initialise (a, 100, 42); // assign the value 42 to all elements of a
Note that the pointer arithmetic still occurs, it's just hidden from the programmer. The array suffix operator is merely sugar-coating. As far as the compiler is concerned, the above is equivalent to the following:
// initialise the array referred to by a of length s elements with the value v void initialise_all (int* a, size_t s, int v)
{
for (size_t idx=0; idx<size; ++idx) // idx is the zero-based index
{
*(a+idx) = value; // pointer arithmetic
}
}
A good compiler will translate the above into the more succinct and highly efficient version:
// initialise the array referred to by a of length s elements with the value v
void initialise_all (int* begin, int* end, int val)
{
while (begin!=end) *(begin++) = val;
}
Although more difficult to read, explicitly using pointer arithmetic yields efficient code regardless of a compiler's ability to optimise your code Behind the Scenes. However, with modern compilers, there should be no difference to the resultant code.
Copyright © 2026 eLLeNow.com All Rights Reserved.