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 прямо сейчас, чтобы подтвердить это, но попробуйте отключить оптимизацию кода и посмотреть, будут ли результаты оставаться неизменными.