Reputation: 1510
I'm reading the section on array arithmetic in K&R and came across something curious. I posted the whole paragraph for context, but I'm mainly focused on the bold part.
If p and q point to members of the same array, then relations like ==, !=, <, >=, etc., work properly. For example, p < q is true if p points to an earlier member of the array than q does. Any pointer can be meaningfully compared for equality or inequality with zero. But the behavior is undefined for arithmetic or comparisons with pointers that do not point to members of the same array. (There is one exception: the address of the first element past the end of an array can be used in pointer arithmetic.)
What is the reason for this exception? Is an extra piece of memory allocated to the end of any array when their size is defined? If so, for what purpose? Is it to end the array with a null character?
Upvotes: 12
Views: 4152
Reputation: 4741
You're simply allowed to compute the address of the object one off the end of the array and it's promised that you won't get into trouble doing that. You're not allowed to dereference that pointer.
An example of where that promise is important is where an object might otherwise be allocated at the very end of memory, so that the address of one off the end would cause arithmetic overflow when you computed the address. If you were to iterate a pointer through that array, then after the final iteration, arithmetic overflow could result in the pointer wrapping around and pointing at NULL.
This could result in comparison results being inverted, and it could trip all sorts of alarm bells with array-bounds checkers, or it could simply compute the wrong address if the CPU uses, for example, saturating arithmetic.
So it's the duty of the compiler and linker to ensure that this cannot happen, and it is the duty of the programmer to ensure that compiler and linker have their responsibility constrained to just that one simple case, and that they don't have to uphold the same guarantee when you run n
elements further off the end.
Upvotes: 1
Reputation: 241631
Is an extra piece of memory allocated to the end of any array when their size is defined?
No. The context that you quoted is important. The exception that you bold is in reference to pointer arithmetic (and relations). It's saying that if you do pointer relations between pointers that do not point to members of the same array, then you get udb. However, there is a single exception to that, which is if either of the pointer are pointing to the first element past the end of the array.
If so, for what purpose?
null
answer since it's presumes a false premise.
Is it to end the array with a null character?
No.
The reason for this is so that comparison to the end of the array is legal, that is, comparisons to &a[sizeof a]
when a
is an array. Note that &a[sizeof a]
is the first element past the end of the array. If p
is a pointer to an element of a
or also the first element past the end of the array, then p
can be compared to &a[sizeof a]
.
I quote from the C99 specification, section 6.5.8.5.
When two pointers are compared, the result depends on the relative locations in the address space of the objects pointed to. If two pointers to object or incomplete types both point to the same object, or both point one past the last element of the same array object, they compare equal. If the objects pointed to are members of the same aggregate object, pointers to structure members declared later compare greater than pointers to members declared earlier in the structure, and pointers to array elements with larger subscript values compare greater than pointers to elements of the same array with lower subscript values. If the expression
P
points to an element of an array object and the expressionQ
points to the last element of the same array object, the pointer expressionQ + 1
compares greater thanP
. In all other cases, the behavior is undefined.
Upvotes: 6
Reputation: 23436
There's no extra memory allocated at the end of the array. It just says that you can you the address marked with 'End' below in pointer arithmetic. Begin points to the first element of the array. End points to the first element past the end of the array.
-----------------
| | | | |
-----------------
^ ^
Begin End
Upvotes: 2
Reputation: 145829
The reason is so you can increment a pointer in a loop like this:
char a[42], *p;
for (p = a; p < &a[sizeof a]; p++) // or p != &a[sizeof a]
{
/* ... */
}
Without the extra rule, this would be undefined behavior because the pointer would be invalid.
Upvotes: 11