Раздел §6.5.3.2 "Операторы адреса и косвенности" ¶3 говорит (только для соответствующего раздела):
Унарный и оператор возвращает адрес своего операнда.... Если операнд является результатом унарного оператора
*, ни этот оператор, ни оператор&не оцениваются, и результат как бы опускался, за исключением того, что ограничения на операторы все еще применяются, и результат не является именующий. Аналогично, если операнд является результатом оператора[], ни оператор&, ни унарный*, который подразумевается[], не оцениваются, а результат выглядит так, как если бы оператор&был удален и оператор[]были заменены на a+....
Это означает, что это:
#define NUM 10
int tmp[NUM];
int *i = tmp;
printf("%ti\n", (ptrdiff_t) (&*i - i) );
printf("%ti\n", (ptrdiff_t) (&i[NUM] - i) );
Должно быть совершенно законным, печатать 0 и NUM (10). Стандарт кажется очень ясным, что оба этих случая должны быть оптимизированы.
Однако, похоже, для оптимизации не требуется следующее:
struct { int a; short b; } tmp, *s = tmp;
printf("%ti\n", (ptrdiff_t) (&s->b - s) );
Это кажется ужасно непоследовательным. Я не вижу причин, чтобы приведенный выше код не печатал дополнение sizeof(int) plus (маловероятное) (возможно, 4).
Упрощение выражения &-> будет таким же концептуальным (IMHO) как &[], простым адресом-плюс-смещением. Это даже смещение, которое будет определяться во время компиляции, а не потенциально runtime с оператором [].
Есть ли что-нибудь в обосновании того, почему это так кажется непоследовательным?