int [] arr = {0}; int value = arr [arr [0] ++]; Значение = 1? - PullRequest
12 голосов
/ 11 августа 2009

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

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

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

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

Видимо, это неправильно. Я пытался найти в спецификациях c # какое-то явное утверждение о том, когда на самом деле происходит приращение, но ничего не нашел.
Второй фрагмент взят из комментария к блогу Эрика на тему:

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

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

  1. Получить данные [i] - 1
  2. Добавьте к значению шага 1 значение 5 - 6
  3. Назначить данные [i] (который по-прежнему 1) значение шага 2 --data [i] = 6
  4. Инкремент i - i = 2

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

Ответы [ 6 ]

17 голосов
/ 11 августа 2009

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

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

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

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

Подробнее см. В разделе 7.5.9 спецификации C # 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 (спецификация C # 3.0).

5 голосов
/ 11 августа 2009

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

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

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

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

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

2 голосов
/ 11 августа 2009
data[i++] // => data[1], then i is incremented to 2

data[1] = data[2] + 5 // => 33 + 5
0 голосов
/ 11 августа 2009

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

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

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

A().B = C()

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

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

StoreInArray(data, i++, data[i] + 5);
0 голосов
/ 11 августа 2009

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

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

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

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

0 голосов
/ 11 августа 2009

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

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

...