Yes. 6.5.2.1 paragraph 1 (C99 standard) describes the arguments to the [] operator:
One of the expressions shall have type "pointer to object
type", the other expression shall have integer type, and the result has type "type".
6.5.2.1 paragraph 2 (emphasis added):
A postfix expression followed by an expression in square brackets
[]is a subscripted designation of an element of an array object. The definition of the subscript operator[]is thatE1[E2]is identical to(*((E1)+(E2))). Because of the conversion rules that apply to the binary+operator, ifE1is an array object (equivalently, a pointer to the initial element of an array object) andE2is an integer,E1[E2]designates theE2-th element ofE1(counting from zero).
It says nothing requiring the order of the arguments to [] to be sane.
Yes. 6.5.2.1 paragraph 1 (C99 standard) describes the arguments to the [] operator:
One of the expressions shall have type "pointer to object
type", the other expression shall have integer type, and the result has type "type".
6.5.2.1 paragraph 2 (emphasis added):
A postfix expression followed by an expression in square brackets
[]is a subscripted designation of an element of an array object. The definition of the subscript operator[]is thatE1[E2]is identical to(*((E1)+(E2))). Because of the conversion rules that apply to the binary+operator, ifE1is an array object (equivalently, a pointer to the initial element of an array object) andE2is an integer,E1[E2]designates theE2-th element ofE1(counting from zero).
It says nothing requiring the order of the arguments to [] to be sane.
In general 2[a] is identical to a[2] and this is guaranteed to be equivalent in both C and C++ (assuming no operator overloading), because as you meantioned it translates into *(2+a) or *(a+2), respectively. Because the plus operator is commutative, the two forms are equivalent.
Although the forms are equivalent, please for the sake of all that's holy (and future maintenance programmers), prefer the "a[2]" form over the other.
P.S., If you do get asked this at an interview, please do exact revenge on behalf of the C/C++ community and make sure that you ask the interviewer to list all trigraph sequences as a precondition to you giving your answer. Perhaps this will disenchant him/her from asking such (worthless, with regard to actually programming anything) questions in the future. In the odd event that the interviewer actually knows all nine of the trigraph sequences, you can always make another attempt to stomp them with a question about the destruction order of virtual base classes - a question that is just as mind bogglingly irrelevant for everyday programming.
language design - why are both index[array] and array[index] valid in C? - Software Engineering Stack Exchange
What is the correct type for array indexes in C? - Stack Overflow
Indexing of array in c programming
arrays - index in c programming - Stack Overflow
First of all, it would help to read dmr's Development of the C Language to get some insights into some of C's quirks, particularly when it comes to array semantics (basically, blame BCPL and B for most of it).
As for the question "[w]hy not just enforce that index[array] is invalid, for clarity's sake," what would such a check buy you in exchange for the cost of performing it? The form almost never appears outside of the IOCCC, so it's not like it's a major problem in production code (compared to the use of, say, gets, or unchecked array accesses (which disallowing i[a] won't help with), or <fill in the blank>). It's not a bug; it doesn't introduce any undefined behavior; it doesn't introduce any security holes not already present with a[i]; the only complaints against it are stylistic in nature.
It's like asking why both T *p and T* p are valid; there is no "why" beyond it being an accident of the language syntax. There's nothing deliberate behind allowing both, it's just a function of how the grammar works. Same with a[i] and i[a]. Professional programmers are (usually) grown-ups, and don't deliberately introduce confusion where it isn't warranted, so most will naturally use a[i].
You're basically trying to guard against a problem that doesn't really exist.
The choice to use array[index] was probably made to follow mathematical convention and the precedent set for arrays by other languages like ALGOL, FORTRAN, and BASIC (the latter two use parentheses instead of brackets). That decision does make the operator an odd duck because it's binary operator but requires that you throw in an additional token after the right-hand expression.
The operator could just as easily have been a single character (@ isn't spoken for in C, so let's call that the "pointer addition operator"). Because, as David Thornley's answer in the aforementioned SO question points out, pointer addition is commutative, a @ 5 and 5 @ a make equal sense. (C forerunner BCPL used ! for this.)
In some ways it's natural to look at the array operator as behaving like a function, which makes sense in the context of languages that do f(x) to call a function and a(i) to access an array. Since operators in C don't work that way, you have to think of it as a commutative, binary operator with baggage.
I think you should use ptrdiff_t for the following reasons
- Indices can be negative. Therefore for a general statement, all unsigned types, including
size_t, are unsuitable. - The type of
p2 - p1isptrdiff_t. Ifi == p2 - p1, then you should be able to getp2back byp2 == p1 + i. Notice that*(p + i)is equivalent top[i]. - As another indication for this "general index type", the type of the index that's used by overload resolution when the builtin
operator[](for example, on a pointer) competes against a user-providedoperator[](for example vector's) is exactly that (http://eel.is/c++draft/over.built#16): >For every cv-qualified or cv-unqualified object type T there exist candidate operator functions of the form
T* operator+(T*, std::ptrdiff_t); T& operator; T* operator-(T*, std::ptrdiff_t); T* operator+(std::ptrdiff_t, T*); T& operator;
EDIT: If you have a really big array or a pointer to a really big memory portion, then my "general index type" doesn't cut it, as it then isn't guaranteed that you can subtract the first element's address from the last element's address. @Ciro's answer should be used then https://stackoverflow.com/a/31090426/34509 . Personally I try to avoid using unsigned types for their non-ability to represent negative edge cases (loop end-values when iterating backwards for example), but this is a kind of religious debate (I'm not alone in that camp, though). In cases where using an unsigned type is required, I must put my religion aside, of course.
I almost always use size_t for array indices/loop counters. Sure there are some special instances where you may want signed offsets, but in general using a signed type has a lot of problems:
The biggest risk is that if you're passed a huge size/offset by a caller treating things as unsigned (or if you read it from a wrongly-trusted file), you may interpret it as a negative number and fail to catch that it's out of bounds. For instance if (offset<size) array[offset]=foo; else error(); will write somewhere it shouldn't.
Another problem is the possibility of undefined behavior with signed integer overflow. Whether you use unsigned or signed arithmetic, there are overflow issues to be aware of and check for, but personally I find the unsigned behavior a lot easier to deal with.
Yet another reason to use unsigned arithmetic (in general) - sometimes I'm using indices as offsets into a bit array and I want to use %8 and /8 or %32 and /32. With signed types, these will be actual division operations. With unsigned, the expected bitwise-and/bitshift operations can be generated.