> It may be a win because to access data using an index you need two
> variables (array address and index) but to access data using pointer you
> need one (just pointer is enough).
At code-generation time the compiler knows well enough how to convert from array notation to pointer arithmetic so no extra registers are used. Indeed it must do so for most architectures. By doing that too early, the compiler loses valuable information about array stride accesses and the like that are critical for transformations like vectorization.
In addition, pointer arithmetic has all sorts of nasty aliasing properties that hamper the compiler's ability to analyze the code.
"But arrays and pointers are the same in C!" some may cry. Well, that's not entirely true and even if it were there's nothing that prohibits the compiler from representing the two very differently in its internal data structures.
> When callbacks are involved it may replace code which uses just
> registers to pass information around with code which uses structure in
This sounds like an ABI issue. Most sane ABIs pass small structs via registers.
> What kind of optimization compiler can apply if I'm using indexes?
Anything that involves analyzing loop inductions and stride patterns.
and about a dozen other transformations. These are the "big improvement" optimizations. Petty things like CSE and copy propagation are important but are done more to enable these big-gain transformations than for their code improvement in and of themselves.
In rare cases the compiler can convert pointer arithmetic back to array indices but this often involves extra bit-twiddling or other arithmetic to recover the original indices and more likely than not aliasing rules get in the way and make it impossible to recover the critical information.
Pointer arithmetic is just generally bad for the compiler. Avoid it if possible.
Incidentally, this is also why it's a very *bad* idea to use unsigned as a loop counter. One is not "giving the compiler more information." On the contrary, by using unsigned one is moving the arithmetic away from normal algebraic rules to the realm of modulo arithmetic. The compiler must then make all kinds of pessimistic assumptions about loop termination and access patterns. This also kills many of the loop transformations listed above.
Of course I am speaking in generalities. One can always find a counter-example. However, the vast majority of codes behave as I describe.