(Self-answered Q&A - this matter keeps popping up)
I assume that the reader is aware of how pointer arithmetic works.
int arr[3] = {1,2,3};
int* ptr = arr;
...
*(ptr + i) = value;
Teachers/C books keep telling me I shouldn't use *(ptr + i)
like in the above example, because "pointers support array style indexing" and I should be using ptr[i] = value;
instead. No argument there - much easier to read.
But looking through the C standard, I find nothing called "array style indexing". In fact, the operator []
is not expecting the either operand to be an array, but instead a pointer or an integer!
6.5.2.1 Array subscripting
Constraints
One of the expressions shall have type ‘‘pointer to complete object type’’, the other expression shall have integer type, and the result has type ‘‘type’’.
Why does the array subscripting operator not expect an array? Is the standard wrong? Is my teacher/C book confused?
You should indeed be using ptr[i]
over *(ptr + i)
for readability reasons. But apart from that, the []
operator is, strictly speaking, actually never used with an array operand.
Arrays, when used in an expression, always "decay" into a pointer to the first element (with some exceptions). C17 6.3.2.1/3, emphasis mine:
Except when it is the operand of the sizeof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue.
Meaning that whenever you type arr[i]
, the operand arr
gets replaced by a pointer to the first element inside that array. This is informally referred to as "array decaying". More info here: What is array decaying?
So whenever you use the []
operator, you use it on a pointer. Always.
The C standard says that this operator is guaranteed to be equivalent to the pointer arithmetic (C17 6.5.2.1/2):
The definition of the subscript operator []
is that E1[E2]
is identical to (*((E1)+(E2)))
.
So whenever we type arr[i]
, it actually gets silently replaced by *(arr+i)
. Where arr
is still a pointer to the first element.
And this is why the description you quoted tells you that either operand could be a pointer and the other an integer. Because obviously it doesn't matter if we type *(arr+i)
or *(i+arr)
- that's equivalent code.
Which in turn allows us to write obfuscated "joke" code like i[arr]
, which is actually valid C and fully equivalent to arr[i]
. But don't write such code in real applications.