Past-the-End Pointers
There is an alternative to passing a pointer to the first element of an array and its size, we could instead pass a pointer to the first element of an array and a pointer to the “end” array.
If we have an array declared as:
float vals[TEMPERATURE_SAMPLE_COUNT];
// (and appropriately initialized by reading from a file)
Notice that
valsdecays to a pointer to the first element of the arrayvals + 0also gives a pointer that points to the first element of the arrayvals + TEMPERATURE_SAMPLE_COUNT - 1gives a pointer that points to the last element of the arrayvals + TEMPERATURE_SAMPLE_COUNTgives a pointer that points to just beyond the last element of the array
It might seem counterintuitive, but it's almost always way more convenient to have “past-the-end” pointers than “last-element” pointers.
Let's look at a revised normalize function that uses this approach.
void normalize(float newmax, float* firstValPtr, float* pastEndPtr) {
float maxval = *firstValPtr;
for (float* p = firstValPtr; p < pastEndPtr; ++p) {
if (*p > maxval) {
maxval = *p;
}
}
for (float* q = firstValPtr; q < pastEndPtr; ++q) {
*q = *q / maxval * newmax;
}
}
Notice that the code in the loop is simpler than before. Instead of saying *(firstValPtr+i) we're now just saying *p.
I think it's more efficient, too! We're avoiding two addition operations every time around the loop!
Possibly. But compilers are good at optimizing code, so we can't really make judgements of that kind without experimental data to back up our claims.
Meh! It might not be more efficient for the generated code, but it's definitely more efficient for me, the programmer! It's less typing to say
*pthan*(firstValPtr+i)or evenfirstValPtr[i].
Calling Our Function
We'd call this function by writing
normalize(height, vals, vals+TEMPERATURE_SAMPLE_COUNT);
C++ also provides an alternative way of writing that code. We can write it as
normalize(height, std::begin(vals), std::end(vals));
Indeed.
std::begin(vals)just returnsvals(the address of the first element), andstd::end(vals)returns the address that lies past-the-end of the array.
Although how the compiler achieves that, for any array of any type, isn't something we'll delve into at this time.
Exercise
Trace through the execution of this code with a memory diagram, include the execution of the normalize function.
int main() {
float stuff[4]{5.0f, 1.0f, 4.0f, 20.0f};
float desired_max = 100.0f;
normalize(desired_max, stuff, stuff+4);
return 0;
}
Before we do that, is
float stuff[4]{5.0f, 1.0f, 4.0f, 20.0f};the same as
float stuff[4] = {5.0f, 1.0f, 4.0f, 20.0f};or not? And also, what does that
fmean at the end of the numbers?
Yes, they're the same. The second version is the older C-style way of saying it. Both syntaxes are widely used in practice.
Saying
5.0fmeans “this is afloatliteral for the number 5—if we'd just said5.0it would be adoubleliteral, and it would have to be converted to afloatto put it in the array.
This video traces through the execution of the code and shows the memory diagram at each step.
Here's the final memory diagram:

(When logged in, completion status appears here.)