Int [] arr = {0}; int value = arr [arr [0] ++]; Значение = 1?

Сегодня я прикрепил статью Эрик Липперт, где он пытался очистить миф между приоритетами операторов и порядком оценки, В конце было два фрагмента кода, которые меня смутили, вот первый фрагмент:

      int[] arr = {0};
      int value = arr[arr[0]++];

Теперь, когда я думаю о значении значения переменной, я просто вычисляю его как единое целое. Вот как я думал, что это работает.

  • Сначала объявите arr как массив int с одним элементом внутри него; это значение элемента равно 0.
  • Вторые получают значение arr [0] --0 в этот случай.
  • Третий получить значение arr [значение шага 2] (который все еще равен 0) --gets arr [0] еще раз - до 0.
  • Четвертое назначение значения шага 3 (0) к значению переменной. --value = 0 сейчас
  • Добавьте к значению шага 2 1 - Теперь arr [0] = 1.

По-видимому, это неправильно. Я попытался выполнить поиск в спецификациях С# для некоторого явного утверждения о том, когда инкремент на самом деле происходит, но не нашел.

Второй фрагмент из комментария Eric сообщение в блоге на тему:

 int[] data = { 11, 22, 33 }; 
 int i = 1;
 data[i++] = data[i] + 5;

Теперь, как я думаю, эта программа выполнит - после объявления массива и назначения 1 в i. [plz bear with me]

  • Получить данные [i] --1
  • Добавьте к значению шага 1 значение 5 - 6
  • Присвоить данные [i] (который по-прежнему равен 1) значение шага 2 - данные [i] = 6
  • Приращение я - я = 2

По моему мнению, этот массив теперь должен содержать значения {11, 27, 33}. Однако, когда я зациклился, чтобы напечатать значения массива, я получил: {11, 38, 33}. Это означает, что шаг post произошел до разыменования массива!

Как так? Разве этот пост не должен быть пост? то есть после всего остального.
Что я пропущу парней?

Ответ 1

Операция postincrement происходит как часть оценки общего выражения. Это побочный эффект, который возникает после оценки значения, но до того, как будут оценены другие выражения.

Другими словами, для любого выражения E, E ++ (если законное) представляет что-то вроде (псевдокода):

T tmp = E;
E += 1;
return tmp;

Это все части оценки E ++, прежде чем что-либо еще будет оценено.

Подробнее см. раздел 7.5.9 спецификации С# 3.0.


Кроме того, для операций присваивания, где LHS классифицируется как переменная (как в этом случае), LHS оценивается до оценки RHS.

Итак, в вашем примере:

int[] data = { 11, 22, 33 }; 
int i = 1;
data[i++] = data[i] + 5;

эквивалентно:

int[] data = { 11, 22, 33 }; 
int i = 1;
// Work out what the LHS is going to mean...
int index = i;
i++;
// We're going to assign to data[index], i.e. data[1]. Now i=2.

// Now evaluate the RHS
int rhs = data[i] + 5; // rhs = data[2] + 5 == 38

// Now assign:
data[index] = rhs;

Соответствующий бит спецификации для этого - раздел 7.16.1 (спецификация С# 3.0).

Ответ 2

Для первого фрагмента последовательность:

  • Объявите arr, как вы описали:
  • Получить значение arr [0], которое равно 0
  • Увеличьте значение arr [0] до 1.
  • Получить значение arr [(результат # 2)], которое является arr [0], которое (за # 3) равно 1.
  • Сохраните результат в value.
  • value = 1

Во втором фрагменте оценка по-прежнему остается справа.

  • Где мы храним результат? В данных [i ++], которые являются данными [1], но теперь я = 2
  • Что мы добавляем? данных [i] + 5, которые теперь являются данными [2] + 5, что составляет 38.

Недостающая часть состоит в том, что "пост" не означает "после ВСЕГО". Это просто означает "сразу после получения текущего значения этой переменной". Посту-приращение, происходящее "в середине" строки кода, полностью нормальное.

Ответ 3

data[i++] // => data[1], then i is incremented to 2

data[1] = data[2] + 5 // => 33 + 5

Ответ 4

Я бы ожидал, что оператор post-increment добавит переменную после использования ее значения. В этом случае переменная увеличивается до второй ссылки на переменную.

Если это не так, вы можете написать

data[i++] = data[i++] + data[i++] + data[i++] + 5

Если бы это было так, как вы говорите, тогда вы могли бы удалить оператор инкремента, потому что в инструкции, о которой я сообщал, ничего не происходит.

Ответ 5

Вы должны думать о назначениях в три этапа:

  • Оценить левую сторону (= получить адрес, где значение должно быть сохранено)
  • Оцените правую сторону.
  • Назначьте значение с шага 2 в ячейке памяти с шага 1.

Если у вас есть что-то вроде

A().B = C()

Затем сначала запустится A(), затем запустится C(), а затем будет запущен установщик свойств B.

По существу, вы должны думать о своем утверждении как

StoreInArray(data, i++, data[i] + 5);

Ответ 6

Причиной может быть то, что некоторые компиляторы оптимизируют я ++ как ++ i. В большинстве случаев конечный результат один и тот же, но мне кажется, что это один из тех редких случаев, когда компилятор ошибается.

У меня нет доступа к Visual Studio прямо сейчас, чтобы подтвердить это, но попробуйте отключить оптимизацию кода и посмотреть, будут ли результаты оставаться неизменными.