Чому це переходить у нескінченну петлю?


493

У мене є такий код:

public class Tests {
    public static void main(String[] args) throws Exception {
        int x = 0;
        while(x<3) {
            x = x++;
            System.out.println(x);
        }
    }
}

Ми знаємо , що він повинен бути прописаний тільки x++або x=x+1, але x = x++він повинен першим атрибутом xдо себе, а потім збільшити його. Чому це xпродовжується 0як значення?

--оновити

Ось байт-код:

public class Tests extends java.lang.Object{
public Tests();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[])   throws java.lang.Exception;
  Code:
   0:   iconst_0
   1:   istore_1
   2:   iload_1
   3:   iconst_3
   4:   if_icmpge   22
   7:   iload_1
   8:   iinc    1, 1
   11:  istore_1
   12:  getstatic   #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   15:  iload_1
   16:  invokevirtual   #3; //Method java/io/PrintStream.println:(I)V
   19:  goto    2
   22:  return

}

Я прочитаю про інструкції, щоб спробувати зрозуміти ...


8
Я підозрюю, що відбувається: 1. завантажуйте х в реєстр (= 0); 2. приріст x (x = 1); 3. збережіть значення регістра в x (x = 0). У C / C ++ це не визначене поведінка, оскільки немає формальної точки послідовності, яка б визначала порядок 2 і 3. Сподіваємось, хтось може процитувати вам щось еквівалентне з специфікації Java.
Rup

19
Ми спробували це на C ++, щоб побачити, що буде, і він друкує 1,2,3 та виходить. Я цього не очікував. Я припускаю, що це залежить від компілятора, оскільки це не визначена поведінка. Ми використовували gnu g ++.
скорботи

13
@saj x++- пост-інкремент; x=- присвоєння результату ; результат від x++оригінального x(і є побічний ефект приросту, але це не змінює результату), так що це може бути витлумачено якvar tmp = x; x++; x = tmp;
Марк Gravell

5
Тепер, коли у мене є популярне питання, про яке я шкодую, (повторні) відповіді не припиняють надходити навіть після обрання правильної відповіді. Мій екран "Останні дії" наповнений тими самими відповідями, і ще більше ...
Том Бріто,

3
@Rob Vermeulen ви можете прочитати повне запитання, перш ніж коментувати ..;) Це був код, який зробив мій студент, і мені було цікаво, чому така поведінка.
Том Бріто

Відповіді:


354

Примітка : Спочатку я розмістив код C # у цій відповіді для ілюстрації, оскільки C # дозволяє передавати intпараметри за посиланням на refключове слово. Я вирішив оновити його фактичним юридичним кодом Java, використовуючи перший MutableIntклас, який я знайшов у Google, щоб наблизитись до того, що refробить у C #. Я не можу реально сказати, чи допомагає це чи шкодить відповіді. Я скажу, що особисто я ще не зробив так багато розробок Java; тому, наскільки я знаю, може бути набагато більше ідіоматичних способів проілюструвати цю точку.


Можливо, якщо ми випишемо метод зробити еквівалент того, що x++він робить, це зробить це зрозумілішим.

public MutableInt postIncrement(MutableInt x) {
    int valueBeforeIncrement = x.intValue();
    x.add(1);
    return new MutableInt(valueBeforeIncrement);
}

Правильно? Збільшити передане значення і повернути початкове значення: ось визначення оператора післярозвитку.

Тепер давайте подивимося, як ця поведінка відтворюється у вашому прикладі коду:

MutableInt x = new MutableInt();
x = postIncrement(x);

postIncrement(x)робить що? Зростання x, так. А потім повертає те, що x було до приросту . Потім це повертається значення присвоюється x.

Тож порядок призначень присвоєно x0, то 1, то 0.

Це може бути зрозуміліше, якщо ми перепишемо вище:

MutableInt x = new MutableInt();    // x is 0.
MutableInt temp = postIncrement(x); // Now x is 1, and temp is 0.
x = temp;                           // Now x is 0 again.

Ваша фіксація на тому, що замінюючи xліву частину вищезазначеного завдання на y", ви бачите, що спочатку він збільшується x, а пізніше приписує це y", вражає мене як заплутана. Це не xпризначено y; це значення, раніше присвоєнеx . Дійсно, введення ін'єкцій yне відрізняється від описаного вище сценарію; ми просто отримали:

MutableInt x = new MutableInt();    // x is 0.
MutableInt y = new MutableInt();    // y is 0.
MutableInt temp = postIncrement(x); // Now x is 1, and temp is 0.
y = temp;                           // y is still 0.

Тож зрозуміло: x = x++фактично не змінюється значення x. Завжди це призводить до того, що x має значення x 0 , потім x 0 + 1, а потім x 0 знову.


Оновлення : Між іншим, щоб у вас не виникло сумнівів, що xколи-небудь присвоюється 1 "між" операцією збільшення та призначенням у наведеному вище прикладі, я зібрав швидку демонстрацію, щоб проілюструвати, що це проміжне значення дійсно "існує", хоча воно буде ніколи не бути "поміченим" на виконуваній нитці.

Демо-виклики x = x++;в циклі, тоді як окремий потік постійно друкує значення xконсолі.

public class Main {
    public static volatile int x = 0;

    public static void main(String[] args) {
        LoopingThread t = new LoopingThread();
        System.out.println("Starting background thread...");
        t.start();

        while (true) {
            x = x++;
        }
    }
}

class LoopingThread extends Thread {
    public @Override void run() {
        while (true) {
            System.out.println(Main.x);
        }
    }
}

Нижче наведено уривок виходу вищезгаданої програми. Помітьте нерегулярне виникнення як 1, так і 0.

Початкова нитка фону ...
0
0
1
1
0
0
0
0
0
0
0
0
0
0
1
0
1

1
Вам не потрібно створювати клас, щоб пройти посилання в java (хоча це, безумовно, спрацює). Ви можете використовувати Integerклас, який є частиною стандартної бібліотеки, і він навіть має перевагу бути автоматичним коробкою до та int майже прозоро.
rmeador

3
@rmeador Integer незмінний, тому ви все одно не можете змінити його значення. Однак AtomicInteger є змінним.
ILMTitan

5
@Dan: До речі, xу вашому останньому прикладі потрібно задекларувати volatile, інакше це невизначена поведінка та бачення 1s - це конкретна реалізація.
axtavt

4
@burkestar: Я не вважаю, що посилання в цьому випадку є цілком підходящим, оскільки це питання Java і (якщо я не помиляюсь) поведінка насправді не визначена в C ++.
Дан Тао

5
@Tom Brito - в C це не визначено ... це ++ можна зробити до або після призначення. Практично кажучи, може бути компілятор, який робить те саме, що і Java, але ви не хотіли б робити на це ставку.
detly

170

x = x++ працює наступним чином:

  • Спочатку він оцінює експресію x++. Оцінка цього виразу виробляє значення виразу (яке є значенням xдо збільшення) та збільшення кроків x.
  • Пізніше він присвоює значення виразу x, замінюючи збільшене значення.

Отже, послідовність подій виглядає наступним чином (це фактичний декомпільований байт-код, як створено javap -cмоїми коментарями):

   8: iload_1 // Запам’ятайте поточне значення x у стеці
   9: iinc 1, 1 // Increment x (не змінює стек)
   12: istore_1 // Записати перезаписане значення з стека в x

Для порівняння x = ++x:

   8: iinc 1, 1 // Приріст x
   11: iload_1 // Натисніть значення x на стек
   12: istore_1 // Поп-значення зі стека до x

якщо ви зробите тест, ви можете побачити, що він спочатку збільшується, а пізніше атрибути. Тому він не повинен приписувати нуль.
Том Бріто

2
@Том у цьому справа - адже це все єдина послідовність, вона робить речі в не очевидному (і, мабуть, невизначеному) порядку. Намагаючись перевірити це, ви додаєте точку послідовності та отримуєте іншу поведінку.
Rup

Що стосується вихідного байтового коду: зауважте, що iincприріст змінної, він не збільшує значення стека, а також не залишає значення на стеці (на відміну від майже всіх інших арифметичних оп). Ви можете додати код, сформований ++xдля порівняння.
Анон

3
@Rep Це може бути не визначено в C або C ++, але в Java це добре визначено.
ILMTitan


104

Це відбувається тому, що значення xзовсім не збільшується.

x = x++;

еквівалентно

int temp = x;
x++;
x = temp;

Пояснення:

Давайте розглянемо байт-код для цієї операції. Розглянемо зразок класу:

class test {
    public static void main(String[] args) {
        int i=0;
        i=i++;
    }
}

Тепер запустивши розбиральник класів на цьому, ми отримаємо:

$ javap -c test
Compiled from "test.java"
class test extends java.lang.Object{
test();
  Code:
   0:    aload_0
   1:    invokespecial    #1; //Method java/lang/Object."<init>":()V
   4:    return

public static void main(java.lang.String[]);
  Code:
   0:    iconst_0
   1:    istore_1
   2:    iload_1
   3:    iinc    1, 1
   6:    istore_1
   7:    return
}

Тепер Java VM заснований на стеці, що означає, що для кожної операції дані будуть висунуті на стек, а зі стека дані вискакують для виконання операції. Існує також інша структура даних, як правило, масив для зберігання локальних змінних. Локальні змінні задаються ідентифікаторами, які є лише індексами масиву.

Давайте подивимося на мнемоніки в main()методі:

  • iconst_0: Постійне значення 0 висувається на стек.
  • istore_1: Верхній елемент стеку вискакується і зберігається в локальній змінній з індексом, 1
    який є x.
  • iload_1: Значення в розташуванні 1, значення x якого є 0, висувається в стек.
  • iinc 1, 1: Значення в місці пам'яті 1збільшується на 1. Так xтепер стає 1.
  • istore_1: Значення у верхній частині стека зберігається у пам'яті 1. Тобто 0присвоюється x перезапису його збільшення.

Отже, значення xне змінюється, що призводить до нескінченного циклу.


5
Насправді він збільшується (у цьому значення ++), але змінна буде перезаписана пізніше.
Прогман

10
int temp = x; x = x + 1; x = temp;краще не використовувати тавтологію у своєму прикладі.
Скотт Чемберлен

52
  1. Позначення префікса збільшуватиме змінну, перш ніж вираження буде оцінено.
  2. Позначення Postfix збільшиться ПІСЛЯ оцінки вираження.

Однак " =" має нижчий пріоритет оператора, ніж " ++".

Тож x=x++;слід оцінити так

  1. x підготовлено до завдання (оцінено)
  2. x приріст
  3. Попереднє значення, xпризначене для x.

Це найкраща відповідь. Деяка розмітка допомогла б їй виділитися трохи більше.
Джастін Форс

1
Це неправильно. Йдеться не про пріоритет. ++має вищий пріоритет, ніж =у C і C ++, але твердження не визначено в цих мовах.
Метью Флашен

2
Оригінальне запитання стосується Яви
Джейді

34

Жодна з відповідей там, де досить помітно, так ось що:

Коли ви пишете int x = x++, ви не призначите xсебе за новим значенням, ви присвоюєте xсобі зворотне значення x++виразу. Що, як виявляється, є первісною цінністю x, про що натякав у відповіді Колін Кокрайн .

Для задоволення протестуйте наступний код:

public class Autoincrement {
        public static void main(String[] args) {
                int x = 0;
                System.out.println(x++);
                System.out.println(x);
        }
}

Результат буде

0
1

Повернене значення виразу - це початкове значення x, яке дорівнює нулю. Але згодом, читаючи значення x, ми отримуємо оновлене значення, тобто одне.


Я спробую зрозуміти рядки байт-коду, подивіться моє оновлення, щоб було зрозуміло .. :)
Том Бріто,

Використання println () було дуже корисним для мене в розумінні цього.
ErikE

29

Це вже добре пояснили інші. Я просто включаю посилання на відповідні розділи специфікації Java.

x = x ++ - це вираз. Java дотримуватиметься порядку оцінки . Спочатку буде оцінено вираз x ++, який збільшить x і встановить значення результату до попереднього значення x . Тоді він призначить результат вираження змінній x. Зрештою, х повертається до попереднього значення.


1
+1. Це, безумовно, найкраща відповідь на власне питання "Чому?"
Метью Флашен

18

Ця заява:

x = x++;

оцінює так:

  1. Натисніть xна штабель;
  2. Приріст x;
  3. Поп xіз стека.

Отже значення не змінюється. Порівняйте це з:

x = ++x;

який оцінюється як:

  1. Приріст x;
  2. Натисніть xна штабель;
  3. Поп xіз стека.

Що ви хочете, це:

while (x < 3) {
  x++;
  System.out.println(x);
}

13
Однозначно правильна реалізація, але питання "чому?".
p.campbell

1
У вихідному коді використовували пост-приріст на x, а потім присвоювали його x. x буде прив’язаний до x до приросту, тому він ніколи не змінить значення.
wkl

5
@cletus Я не прихильник, але ваша початкова відповідь не містила пояснень. Він просто сказав "x ++".
Петро Мінчев

4
@cletus: Я не сказав, але ваша відповідь спочатку була лише x++фрагментом коду.
p.campbell

10
Пояснення теж неправильне. Якщо коду спочатку було призначено x до x, а потім збільшено x, воно буде добре працювати. Просто змініть x++;своє рішення x=x; x++;і ви робите те, що стверджуєте, що робить оригінальний код.
Wooble

10

Відповідь досить проста. Це пов'язано з порядком оцінки речей. x++повертає значення xпотім з кроком x.

Отже, значення виразу x++є 0. Отже, ви призначаєте x=0кожен раз у циклі. Безумовно, x++збільшує це значення, але це відбувається перед призначенням.


1
Нічого собі, на цій сторінці так багато деталей, коли відповідь коротка і проста, тобто ця.
Чарльз Гудвін

8

З http://download.oracle.com/javase/tutorial/java/nutsandbolts/op1.html

Оператори збільшення / зменшення можуть бути застосовані до (префікса) або після (постфіксу) операнду. Результат коду ++; і ++ результат; обидва в результаті закінчуватимуться збільшенням одиниці. Єдина відмінність полягає в тому, що версія префікса (++ результат) оцінюється до прирощеного значення, тоді як версія постфіксу (результат ++) оцінюється до вихідного значення . Якщо ви просто виконуєте простий приріст / декремент, не важливо, яку версію ви обрали. Але якщо ви використовуєте цей оператор в частині більшого виразу, той, який ви вибрали, може суттєво змінити.

Для ілюстрації спробуйте наступне:

    int x = 0;
    int y = 0;
    y = x++;
    System.out.println(x);
    System.out.println(y);

Який надрукує 1 та 0.


1
Справа не в оцінці, а в порядку магазинів.
Rup

2
Я не погоджуюсь. Якщо x = 0, то x ++ повернеться 0. Тому x = x ++ призведе до x = 0.
Колін Кокран

Rup з цього приводу має рацію. Саме в цьому випадку йдеться про замовлення магазинів. y = x ++ не те саме, що x = x ++; На останньому x присвоюється 2 значення в одному виразі. Лівій руці x присвоюється результат оцінки виразу x ++, який дорівнює 0. Правий х х збільшується до 1. У якому порядку ці 2 призначення ставляться, в чому полягає проблема. З попередніх дописів видно, що так працює: eval = x ++ => eval == 0: приріст право x => x == 1: зліва x = eval => x == 0
Michael Ekoka

7

Ви ефективно отримуєте таку поведінку.

  1. схопимо значення x (яке дорівнює 0) як "результат" правої сторони
  2. приріст значення x (тому x зараз дорівнює 1)
  3. призначте результат правого боку (який було збережено як 0) до x (x зараз 0)

Ідея полягає в тому, що оператор після збільшення (x ++) збільшує цю змінну, про яку йде мова, ПІСЛЯ повертає її значення для використання в рівнянні, в якому він використовується.

Редагувати: Додавання трохи через коментар. Розглянемо це наступним чином.

x = 1;        // x == 1
x = x++ * 5;
              // First, the right hand side of the equation is evaluated.
  ==>  x = 1 * 5;    
              // x == 2 at this point, as it "gave" the equation its value of 1
              // and then gets incremented by 1 to 2.
  ==>  x = 5;
              // And then that RightHandSide value is assigned to 
              // the LeftHandSide variable, leaving x with the value of 5.

Гаразд, але що визначає порядок кроків 2 та 3?
Rup

@Rup - Мова визначає це. Права частина рівняння оцінюється спочатку (у цьому випадку "х ++"), а результат присвоюється змінній з лівого боку. Ось так працює мова. Що стосується рівняння "x ++" "повернення" x для рівняння, то так працює оператор приросту postfix (поверніть значення x, а потім збільшуйте його). Якби це було "--x", то воно було б (збільшення x, а потім поверне значення). Повернення не є правильним словом там, але ви отримуєте ідею.
RHSeeger

7

Вам не потрібен машинний код, щоб зрозуміти, що відбувається.

Відповідно до визначень:

  1. Оператор присвоєння оцінює вираз правого боку та зберігає його у тимчасовій змінній.

    1.1. Поточне значення х копіюється в цю тимчасову змінну

    1.2. x збільшується зараз.

  2. Потім тимчасова змінна копіюється в ліву частину виразу, що є випадково х! Тож тому старе значення х знову копіюється в себе.

Це досить просто.


5

Це тому, що він ніколи не збільшується в цьому випадку. x++спочатку використовуватиме його значення перед збільшенням, як у цьому випадку, воно буде таким:

x = 0;

Але якщо ви ++x;це зробите, це збільшиться.


якщо ви зробите тест, ви можете побачити, що він спочатку збільшується, а пізніше атрибути. Тому він не повинен приписувати нуль.
Том Бріто

1
@Tom: дивіться мою відповідь - я показую в тесті, що x ++ фактично повертає старе значення x. Ось там і ламається.
Роберт Мунтяну

"якщо ви зробите тест" - деякі люди, здається, думають, що тест, написаний на C, говорить нам, що робитиме Java, коли він навіть не скаже нам, що C буде робити.
Джим Балтер

3

Значення залишається рівним 0, оскільки значення x++дорівнює 0. У цьому випадку не має значення x, збільшується чи ні, значення x=0виконується. Це замінить тимчасове збільшення значення x(яке було 1 за "дуже короткий час").


Але x ++ - це поштова операція. Таким чином, х потрібно було б збільшити після завершення завдання.
Сагар V

1
@Sagar V: лише для виразу x++, а не для всього завданняx=x++;
Прогман

Ні, я думаю, що її потрібно збільшувати лише після того, як буде прочитано значення x, яке буде використано у призначенні.
Rup

1

Це працює так, як ви очікуєте від іншого. Це різниця між префіксом та постфіксом.

int x = 0; 
while (x < 3)    x = (++x);

1

Подумайте про x ++ як виклик функції, який "повертає" те, що було X до приросту (саме тому його називають пост-збільшенням).

Отже, порядок операцій:
1: кешувати значення x перед збільшенням
2: приріст x
3: повернути кешоване значення (x до його збільшення)
4: значення повернення призначається x


Гаразд, але що визначає порядок кроків 3 та 4?
Rup

"повертає те, що було до приросту Х", невірно, дивіться моє оновлення
Том Бріто,

Насправді кроки 3 та 4 не є окремими операціями - це насправді не виклик функції, який повертає значення, він просто допомагає думати про це таким чином. Кожен раз, коли у вас є завдання, праву частину "оцінюють", тоді результат присвоюють лівій частині, результат оцінювання можна вважати зворотним значенням, оскільки це допомагає зрозуміти порядок операцій, але це насправді не так .
jhabbott

На жаль, правда. Я мав на увазі кроки 2 та 4 - чому повернене значення зберігається у верхній частині нарощеного значення?
Rup

1
Це частина визначення операції по призначенню, спочатку права рука оцінюється повністю, потім результат присвоюється лівій частині.
jhabbott

1

Коли значення ++ знаходиться на rhs, результат повертається перед збільшенням числа. Перейдіть на ++ x, і це було б добре. Java оптимізувала б це для виконання однієї операції (призначення x до x), а не збільшення.


1

Наскільки я можу бачити, помилка виникає внаслідок присвоєння, що перекриває збільшене значення, зі значенням до збільшення, тобто воно скасовує приріст.

Зокрема, вираз "x ++" має значення "x" перед збільшенням на відміну від "++ x", яке має значення "x" після збільшення.

Якщо ви зацікавлені в дослідженні байт-коду, ми розглянемо три питання, про які йдеться:

 7:   iload_1
 8:   iinc    1, 1
11:  istore_1

7: iload_1 # Поставить значення 2-ї локальної змінної на стек
8: iinc 1,1 # збільшить 2-ю локальну змінну на 1, зауважте, що вона залишає стек недоторканим!
9: istore_1 # Виведе верхню частину стека і збереже значення цього елемента у 2-й локальній змінній
(Ви можете прочитати ефекти кожної інструкції JVM тут )

Ось чому наведений вище код буде циклічно нескінченним, тоді як версія з ++ x не буде. Байт-код для ++ x повинен виглядати зовсім інакше, наскільки я пам'ятаю з компілятора 1.3 Java, про який писав трохи більше року тому, байт-код повинен мати щось подібне:

iinc 1,1
iload_1
istore_1

Тому просто поміняючи два перші рядки, змінює семантику таким чином, що значення, залишене у верхній частині стека, після збільшення (тобто «значення» виразу) є значенням після збільшення.


1
    x++
=: (x = x + 1) - 1

Тому:

   x = x++;
=> x = ((x = x + 1) - 1)
=> x = ((x + 1) - 1)
=> x = x; // Doesn't modify x!

Тоді як

   ++x
=: x = x + 1

Тому:

   x = ++x;
=> x = (x = x + 1)
=> x = x + 1; // Increments x

Звичайно, кінцевий результат такий самий, як просто x++;або ++x;на лінії сам по собі.



0

Цікаво, чи є в специфікації Java щось, що точно визначає поведінку цього. (Очевидним наслідком цього твердження є те, що я лінивий перевіряти.)

Зверніть увагу на байт-код Тома, ключові рядки - 7, 8 та 11. Рядок 7 завантажує x у стек обчислень. З кроком 8 рядка x. Рядок 11 зберігає значення з стека назад до x. У звичайних випадках, коли ви не присвоюєте собі значення назад, я не думаю, що це було б причиною, чому ви не змогли б завантажувати, зберігати, а потім збільшувати. Ви отримали такий же результат.

Припустимо, у вас був більш нормальний випадок, коли ви написали щось на кшталт: z = (x ++) + (y ++);

Чи було це сказано (псевдокод, щоб пропустити технічні характеристики)

load x
increment x
add y
increment y
store x+y to z

або

load x
add y
store x+y to z
increment x
increment y

має бути неактуальним. Будь-яка реалізація повинна бути дійсною, я думаю.

Я б був надзвичайно обережним щодо написання коду, який залежить від такої поведінки. Мені це здається дуже залежним від реалізації, між "тріщинами в техніці". Єдиний раз, коли це зміниться, якщо ви зробили щось божевільне, як, наприклад, приклад тут, або якщо у вас були два потоки, і ви залежали від порядку оцінки в виразі.


0

Я думаю, тому що у Java ++ вищий пріоритет, ніж = (призначення) ... Це? Подивіться на http://www.cs.uwf.edu/~eelsheik/cop2253/resources/op_precedence.html ...

Точно так само, якщо ви пишете x = x + 1 ... + має вищий пріоритет, ніж = (призначення)


Це не питання пріоритетності. ++також має більший пріоритет, ніж =у C та C ++, але твердження не визначено.
Меттью Флашен

0

x++Вираз має значення x. ++Частина впливає на вартість після оцінки , а НЕ після заяви . тому x = x++ефективно перекладається на

int y = x; // evaluation
x = x + 1; // increment part
x = y; // assignment

0

Перед збільшенням значення на одиницю значення присвоюється змінній.


0

Це відбувається тому, що він збільшується. Це означає, що змінна збільшується після оцінки виразу.

int x = 9;
int y = x++;

x зараз 10, але y дорівнює 9, значення x до його збільшення.

Детальніше дивіться у розділі Визначення посту збільшення .


1
Ваш x/ yприклад відрізняється від реального коду, і різниця є актуальною. Ваше посилання навіть не згадує Java. Для двох мов це робить згадати, заяву в питанні не визначено.
Меттью Флашен

0

Перевірте наведений нижче код,

    int x=0;
    int temp=x++;
    System.out.println("temp = "+temp);
    x = temp;
    System.out.println("x = "+x);

вихід буде,

temp = 0
x = 0

post incrementозначає приріст значення і повернення значення перед приростом . Ось чому значення tempє 0. То що робити, якщо temp = iце і є в циклі (крім першого рядка коду). так само, як у питанні !!!!


-1

Оператор приросту застосовується до тієї ж змінної, яку ви призначаєте. Ось просить клопоту. Я впевнений, що ви можете бачити значення вашої змінної x під час роботи цієї програми .... це повинно дати зрозуміти, чому цикл ніколи не закінчується.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.