Как решить постфиксный и префиксный оператор выражения?
Я написал следующий код:
int main(){
int i=-1,j=-1,k=0,l=2,m;
m = i++ && j++ && k++ || l++;
printf("%d %d %d %d %d",i,j,k,l,m);
i=-1,j=-1,k=0,l=2,m;
m = ++i && ++j && ++k || ++l;
printf("\n%d %d %d %d %d",i,j,k,l,m);
return(0);
}
и я получил следующий результат:
0 0 1 3 1
0 -1 0 3 1
как я знаю, постфиксные и префиксные операторы решаются после точки с запятой, т.е. исходные значения используются в выражении, а затем переменные разрешаются сами. В таком случае
i++ && j++
должно быть равно
++i && ++j
и оба должны быть эквивалентны
i && j;
i = i+1;
j = j+1;
и, следовательно, результат для двух выражений должен быть одинаковым. Но это не так. Пожалуйста, кто-нибудь может помочь мне с тем, где у меня есть неправильная концепция.
2 ответа
i++ && j++
определенно не эквивалентно ++i && ++j
,
Возьмите случай, когда оба равны нулю - первый результат:
0 && j++
результат 0 (а из-за короткого замыкания j даже не будет увеличен).
Во втором случае вы получите
1 && 1
дает 1 результат.
Имея это в виду, давайте рассмотрим ваши примеры (для удобства чтения добавлены пробелы, точки с запятой и символы новой строки):
Сначала:
int i = -1; int j = -1; int k = 0; int l = 2; int m = i++ && j++ && k++ || l++;
Давайте сначала заключим в скобки полностью, чтобы было легче иметь дело с:
int m = ((i++ && j++) && k++) || l++;
Итак, что произойдет? Во-первых,
i++
,i
увеличивается (и становится0
), но поскольку это постинкремент, результат выражения-1
, Это дает:int m = ((-1 && j++) && k++) || l++;
Так как левая сторона этого
&&
не ноль, правая сторона оценивается.j++
приращенийj
в0
но, опять же, постинкремент означает, что значение выражения-1
,int m = ((-1 && -1) && k++) || l++;
Разрешите это
&&
:int m = (1 && k++) || l++;
Далее правая сторона оставшихся
&&
,k
увеличивается, становясь1
, но выражение дает0
:int m = (1 && 0) || l++;
Разрешите это
&&
:int m = 0 || l++;
И, наконец, потому что левая сторона
||
0, правая сторона оценивается.l
увеличивается, становясь3
, но как постинкремент, дает2
:int m = 0 || 3;
В заключение:
int m = 1;
И по пути мы закончили с:
i = 0; j = 0; k = 1; l = 3;
Объясняя свою первую распечатку.
Далее, давайте посмотрим на второй пример (более сжатый; дайте мне знать, если вы хотите больше подробностей):
int i = -1, j = -1, k = 0, l = 2, m; m = ((++i && ++j) && ++k) || ++l; // parenthesized for readability ^ // i is pre-incremented m = (( 0 && ++j) && ++k) || ++l; ^ // first && operator short-circuits m = (0 && ++k) || ++l; ^ // second && operator short-circuits m = 0 || ++l; ^ // l is pre-incremented m = 0 || 3; ^ // evaluate || operator m = 1;
Результаты:
i = 0 j = -1 k = 0 l = 3 m = 1
Именно то, что вы видели, распечатано.
Проблема в вашем условном операторе:
m=i++&&j++&&k++||l++;
Программа начнет проверку условного оператора (i++ && j++), который равен (-1 && -1), который получается истинным, поэтому он продолжает оператор (true && k++), что означает (true && 0), который является ложным. Затем он проверит условие OR (false || l++), что означает (false || 2). Каждый раз, когда он обрабатывал условия, значения увеличивались с помощью оператора ++.
Второе утверждение, однако...
m=++i&&++j&&++k||++l;
Программа запустила условное условие (++i && ++j), которое равно (0 && ++j). Поэтому он сразу увидел первое условие ++ i как ложное и сразу расценил остальную часть блока (m=++i&&++j&&++k) как ложное, никогда не обрабатывая ++ j или ++ k, как это делал Не нужно, чтобы определить, что блок собирается выйти из строя. Затем он переходит к условию ИЛИ (false || ++l) или (false || 3), что верно, m=1.
В конце концов, второе утверждение только когда-либо обрабатывает ++ i и ++ l, оставляя остальные без изменений.
Я бы немного исследовал что-то под названием "короткое замыкание", так как именно этот термин описывает, почему это происходит.