Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is pointing to one before the first element of an array not allowed in C?

Tags:

arrays

c

pointers

In C, a pointer is allowed to point to any element of an array, as well as to one past the last element of the array; pointing to beyond this is undefined behavior (C11 standard, §6.5.6, paragraph 8).

However, why is pointing to one before the first element of an array not similarly allowed?

P.S: I know that, in order to circumvent the above restriction, one can sometimes declare an array 1 unit larger than needed, then use only positions from 1 on to store elements, and finally leave position 0 just as a guarantee that traversing the array backwards will be safe. However, sometimes we must use an array as given, and then the problem of pointing to one before the first element remains.

like image 436
Pablo M. S. Farias Avatar asked Sep 13 '17 14:09

Pablo M. S. Farias


3 Answers

The topic "a pointer is allowed to point to any element of an array" has been part of the C language for a very long time; it can already be found in the ANSI-C standard / C89 (cf. section 6.3.6 Additive operators, page 46f), which already mentions that within this range implementations must prevent arithmetic overflows. Therein, at the end of page 47, one can also find a footnote that explains the rational behind it:

Another way to approach pointer arithmetic is to first convert the pointer(s) to character pointers: In this scheme the integral expression added to or subtracted from the converted pointer is first multiplied by the size of the object originally pointed to and the resulting pointer is converted back to the original type. For pointer subtraction the result of the difference between the character pointers is similarly divided by the size of the object originally pointed to.

When viewed in this way, implementations need only provide one extra byte (which may overlap another object in the program) just after the end of the object in order to satisfy the "one past the last element" requirement.

Therefrom we may deduce that the reason is preventing arithmetic overflows, which an implementation must avoid in the range of an array and one past (but which obviously does not need to be guaranteed for an element one before the first element of an array).

Why not one before - is probably (I have no proof for that) connected with the fact that pointers ranges could be limited to particular memory segments, and placing objects at the begin of a memory segment together with arithmetics like "one before the first element" could lead to an overflow. I'd say statement "implementations need only provide one extra byte (which may overlap another object in the program) just after the end of the object" is a clear indicator supporting this assumption, as the standard at this time explicitly addressed the "one extra byte"-topic at the end but did not suggest something similar for the begin of memory segments.

like image 195
Stephan Lechner Avatar answered Oct 13 '22 02:10

Stephan Lechner


I'm not sure that it's the rationale, but in practice you would need to either

  • throw away the relational operators on pointers (due to wraparound), or
  • require that no objects of size X be stored at an address less than X, or
  • cut the available memory in half (due to negative addresses).

Neither option is very pleasant.

like image 41
molbdnilo Avatar answered Oct 13 '22 04:10

molbdnilo


The rationale why it is allowed to point one past the array in the first place, is to enable certain kinds of "C++/STL-ish" programming patterns:

int array [n] = ...;
int* begin = array;
int* end = array + n;

for(int* i=begin; i!=end; i++)
...

For situations like the one above it makes sense to point one item past the array. But of course it does not make sense to actually access that item - which would be undefined behavior.

So the above is a special case. However, there exists no situation where it makes sense to point one item before the array.

like image 2
Lundin Avatar answered Oct 13 '22 04:10

Lundin