Я изучал следующий фрагмент кода и пытался написать на бумаге значения, которые i и j получают на каждом шаге, но я не понимаю, почему на 4-м шаге значение j уменьшается с 3 до 2, так как нет оператора декремента ( насколько я понимаю):
#include<stdio.h>
int main() {
int i, j, n;
int v[] = {
-7,
29,
76,
8
};
n = sizeof(v) / sizeof(int);
for (i = 0, j = 1; n > 1 && i < n;
(v[i] > v[j]) && (v[i] = v[i] + v[j] - (v[j] = v[i])), i++, j = j < n - 1 ? --i, j + 1 : i + 1)
printf("i=%d j=%d \n", i, j);
for (i = 0; i < n; i++) printf("%d\t", v[i]);
printf("\n");
return 0;
}
Output:
i=0 j=1
i=0 j=2
i=0 j=3
i=1 j=2
i=1 j=3
i=2 j=3
i=3 j=4
i=3 j=5
i=3 j=6
-7 8 29
Пытался понять, как i и j получают свое значение.
Я не уверен, что этот код стоит изучать. Если бы я столкнулся с этим кодом, первое, что я бы сделал, это упростил этот цикл for
и разбил составные части на более легко усваиваемые операторы.
gcc с усиленными предупреждениями считает, что этот код может демонстрировать неопределенное поведение. Это потому, что v[j] используется и назначается в одной и той же операции, и порядок их не определен.
Я так и подозревал. Когда вы добьетесь этого ума, станет очень легко случайно вызвать UB.
И с этим игра окончена. Я знаю, что некоторые люди не согласны, но я считаю, что бессмысленно изучать код, в котором уже есть UB, потому что его поведение уже не определено.
Над j
нет операции уменьшения, но есть присваивание j
. На самом деле это единственный способ вообще изменить значение j
. Во всяком случае, так оно и было бы, если бы поведение программы было определено иначе.
@Dan X Нет особого смысла исследовать этот глупый код.
Если бы кто-то из моей команды написал подобное выражение цикла for, я бы серьезно задумался о его психическом состоянии :). А теперь серьезно: этот код не выглядит умным — и написание большего количества строк в одной строке или операторе не свидетельствует о хорошем программисте. Это прямо напротив. @RobertHarvey прав на 100%. Перепиши и найдешь ошибки
Код недействителен:
v[i] = v[i] + v[j] - (v[j] = v[i])
присваивает v[j]
и одновременно считывает из v[j]
. Вы не можете этого сделать.j=4
, то доступ к v[j]
недействителен. v
имеет 4 элемента, вы можете получить доступ к индексу 3
.почему на 4-м шаге значение j уменьшается с 3 до 2
Начиная с шага 3: i
увеличивается i++
, поэтому i=1
. Тогда j
просто присваивается значение 2
из ложной ветви троичного выражения. i + 1
равно 2, поэтому y=2
.
> i=0 j=3 n=4
..., i++, j = j < n - 1 ? --i, j + 1 : i + 1
> i++ -> i=1
..., j = j < n - 1 ? --i, j + 1 : i + 1
> j < n - 1 -> 3 < 3 -> false
j = i + 1
> i + 1 -> 1 + 1 -> 2
> j = 2
Я надеюсь, это поможет:
Первая «часть» оператора for устанавливает начальные значения i и j (0 и 1 соответственно).
Третья «часть» оператора for отвечает (в вашем примере кода) за все остальные изменения значений i и j. Эта часть представляет собой выражение с запятой (с тремя подвыражениями), которое оценивается слева направо. Эти подвыражения (в порядке оценки):
(v[i] > v[j]) && (v[i] = v[i] + v[j] - (v[j] = v[i]))
Это означает: если (v[i] > v[j]), то дать v[j] предыдущее значение v[i] [часть: (v[j] = v[i])] и v[i] значение v[j] (часть: v[i] = v[i] + v[j] - (v[j] = v[i]), обратите внимание, что v[j] = v[i] оценивается как v [и]). Другими словами: если v[i] больше, чем v[j], то поменяйте местами значения v[i] и v[j].
i++
Самая простая часть: увеличить значение i на единицу.
j = j < n - 1 ? --i, j + 1 : i + 1
Здесь используется третичный оператор (?:). Если (j < n - 1) верно, то "--i, j+1" (другое выражение с запятой) оценивается (слева направо) и его значение (значение последней/правой части, т.е.: j+ 1) присвоен j. Другими словами: значение i уменьшается на единицу, а j увеличивается на единицу, если j меньше n-1. Если j не меньше n-1, то последняя часть третичного оператора i+1 присваивается j.
Кстати: я думаю, что последняя строка вашего вывода должна быть: «-7 8 29 76», а не «-7 8 29». Итак, этот фрагмент кода сортирует массив v[].
Поведение v[i] = v[i] + v[j] - (v[j] = v[i])
не определено стандартом C в соответствии с правилом C 2018 6.5 2, потому что оно одновременно изменяет значение v[j]
и использует его значение без последовательности между этими двумя применениями.
@EricPostpischil: Вы, конечно, правы, но я пытался объяснить поведение данного образца кода, а не доказывать, что он может работать как программа на C в соответствии со стандартами.
Спасибо за объяснение, я понимаю, что знаю, не мог понять, как j было присвоено значение, если (j<n-1) было ложным. Кроме того, да, код сортирует массив v[], поэтому вывод должен быть таким, как указано!
Это противно
j = j < n - 1 ? --i, j + 1 : i + 1