2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




Начать новую тему Ответить на тему На страницу 1, 2, 3  След.
 
 Наблюдение за префиксным инкрементом
Сообщение26.03.2022, 17:43 
Аватара пользователя


29/05/17
808
Вычислить результат:
Код:
i = 5;
i = ++i + ++i;

в любых языках программирования.
Mental в сообщении #1551110 писал(а):
хотел сделать в виде задачки, чтобы участники форума, попытались решить самостоятельно.

 Профиль  
                  
 
 Posted automatically
Сообщение26.03.2022, 17:59 
Заслуженный участник


09/05/12
25179
 i  Тема перемещена из форума «Computer Science» в форум «Карантин»
по следующим причинам:

- отсутствует формулировка предмета обсуждения.

Исправьте все Ваши ошибки и сообщите об этом в теме Сообщение в карантине исправлено.
Настоятельно рекомендуется ознакомиться с темами Что такое карантин и что нужно делать, чтобы там оказаться и Правила научного форума.

 Профиль  
                  
 
 Posted automatically
Сообщение26.03.2022, 19:54 


20/03/14
12041
 i  Тема перемещена из форума «Карантин» в форум «Программирование»

 Профиль  
                  
 
 Re: Наблюдение за префиксным инкрементом
Сообщение26.03.2022, 21:41 
Заслуженный участник


26/05/14
981
В C и C++ это выражение запрещено. Компилятор выдаёт ошибку. В Java и JavaScript это выражение хорошо определено и вычисляется как

Код:
        int i = 5;
        // i = ++i + ++i;
        int a = ++i;  // a = 6, i = 6
        int b = ++i;  // b = 7, i = 7
        i = a + b;    // i = 13


Во всех языках использование побочных эффектов при вычислении выражения - плохая бессмысленная практика.

 Профиль  
                  
 
 Re: Наблюдение за префиксным инкрементом
Сообщение26.03.2022, 22:01 
Аватара пользователя


29/05/17
808
slavav в сообщении #1551121 писал(а):
В C и C++ это выражение запрещено. Компилятор выдаёт ошибку.

Попробуйте здесь:
https://www.onlinegdb.com/online_c++_compiler

-- 26.03.2022, 22:07 --

А здесь также можно провести исследование для C#:
https://rextester.com/

 Профиль  
                  
 
 Re: Наблюдение за префиксным инкрементом
Сообщение26.03.2022, 22:18 
Заслуженный участник


26/05/14
981
Код:
i = ++i + ++i;
порождает неопределённое поведение. Это значит что результат может быть любой. Можно поизучать машинный код и порассуждать на тему какие могли бы быть результаты при той или иной интерпретации этого выражения, которое, повторюсь, не является корректным выражением на языке C++.

 Профиль  
                  
 
 Re: Наблюдение за префиксным инкрементом
Сообщение27.03.2022, 21:16 
Заблокирован


19/02/13

2388
Mental в сообщении #1551123 писал(а):
здесь:


Выдаёт результат 14. Откуда лишняя единица?
А, понял. Сначала инкремент, потом ещё раз - а переменная-то та же самая. Вот и складывает 7 и 7.

-- 27.03.2022, 21:31 --

Проверил тот же код в CodeBlocks, компилятор GCC. Результат тот же, ошибок нет. Если убрать пробелы между плюсами - ругается, а с пробелами всё работает нормально.
slavav, откуда там неопределённое поведение возьмётся? Вроде всё однозначно трактуется.

-- 27.03.2022, 21:35 --

С постфиксным инкрементом тоже, кстати, никаких ошибок - всё работает по правилам языка.

 Профиль  
                  
 
 Re: Наблюдение за префиксным инкрементом
Сообщение27.03.2022, 21:57 


27/08/14
207
Vladimir-80 в сообщении #1551200 писал(а):
всё работает по правилам языка
По каким правилам? По правилам должно быть так:
Код:
gcc -Wall -Werror -pedantic 1.c
1.c: In function ‘main’:
1.c:4:5: error: operation on ‘i’ may be undefined [-Werror=sequence-point]
    4 |   i = ++i + ++i;
      |   ~~^~~~~~~~~~~
Причём двойной инкремент тут не при чём, даже с одним инкрементом это неопределённое поведение:
Код:
1.c:6:5: error: operation on ‘i’ may be undefined [-Werror=sequence-point]
    6 |   i = ++i;
      |   ~~^~~~~
Т.к. переменная в выражении меняется дважды, и не понятно какое значение она должна принять. Это особенно хорошо видно в случае выражения i = i++. Но даже в случае присвоения другой переменной, всё равно будет UB:
Код:
1.c:6:17: error: operation on ‘i’ may be undefined [-Werror=sequence-point]
    6 |   int j = ++i + ++i;
      |                 ^~~
Т.к. результат зависит от порядка вычисления выражения, а он не определён стандартом.

 Профиль  
                  
 
 Re: Наблюдение за префиксным инкрементом
Сообщение27.03.2022, 22:07 
Заблокирован


19/02/13

2388
Progger в сообщении #1551202 писал(а):
переменная в выражении меняется дважды, и не понятно какое значение она должна принять


А что тут непонятного? Есть ячейка памяти под кодовым названием
Код:
i
, с её содержимым и работаем. Стоит справа от знака равенства префиксный инкремент на эту ячейку? Увеличиваем значение. Ещё раз он же стоит? Повторяем процедуру. Так, с префиксами справились, что там дальше, сложение себя с самой? Да запросто. Куда пишем результат? Туда же? Отлично! Готово, принимайте работу. В Си с этим никаких проблем.

 Профиль  
                  
 
 Re: Наблюдение за префиксным инкрементом
Сообщение27.03.2022, 23:43 
Заслуженный участник
Аватара пользователя


16/07/14
9216
Цюрих
Vladimir-80 в сообщении #1551203 писал(а):
В Си с этим никаких проблем
Это неправда.
C standard, 6.5.2 писал(а):
If a side effect on a scalar object is unsequenced relative to either a different side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined.
89) This paragraph renders undefined statement expressions such as
Используется синтаксис C
i = ++i + 1;

Т.е. в стандарте прямым текстом написано, что это выражение - UB.
Vladimir-80 в сообщении #1551203 писал(а):
Стоит справа от знака равенства префиксный инкремент на эту ячейку? Увеличиваем значение. ... Куда пишем результат? Туда же? Отлично!
Никто не гарантирует, что запись результата в i произойдет "до" увеличения значения.
Вообще нельзя думать, что переменная - это такая коробка, в которой в каждый момент лежит какое-то значение, и если туда что-то положили, то именно оно в ней и лежит, пока туда не положат что-то еще. Это хорошо видно с многопоточностью:
Используется синтаксис C++
x = y = 0;
// Thread 1:
r1 = y.load(std::memory_order_relaxed); // A
x.store(r1, std::memory_order_relaxed); // B
// Thread 2:
r2 = x.load(std::memory_order_relaxed); // C
y.store(42, std::memory_order_relaxed); // D
 
Имеет полное право выдать r1 == 42, r2 == 42, хотя 1) никакое чередование выполнения первого и второго потоков не может дать такой результат; 2) стандарт гарантирует, что каждый отдельный поток работает так как будто выражения выполняются в том порядке, в котором записаны.

 Профиль  
                  
 
 Re: Наблюдение за префиксным инкрементом
Сообщение28.03.2022, 00:07 
Заслуженный участник


26/05/14
981
Цитата из стандарта:
Цитата:
Between the previous and next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression.

Перевод:
Цитата:
Между предыдущей и следующей точками последовательности при вычислении выражения скалярный объект может быть обновлен самое большее один раз.

Стандарт запрещает выражения вида i = ... ++i ...; так как такие выражения обновляют переменную i два раза.

Один из способов скомпилировать наше выражение (так работает Java):
Код:
    int i = 5;

    // i = ++i + ++i;
    int new_i1 = i + 1;   // 6
    i = new_i1;           // 6

    int new_i2 = i + 1;   // 7
    i = new_i2;           // 7

    i = new_i1 + new_i2;  // 13


Ещё один способ:
Код:
    int i = 5;

    // i = ++i + ++i;
    i = i + 1;       // 6
    i = i + 1;       // 7
    i = i + i;       // 14


И ещё один:
Код:
    int i = 5;

    // i = ++i + ++i;
    int old_i1 = i;
    int old_i2 = i;
    int new_i1 = i + 1;   // 6
    int new_i2 = i + 1;   // 6
    i = new_i1 + new_i2;  // 12
    i = old_i1 + 1;       // 6
    i = old_i2 + 1;       // 6


Главная проблема: три присваивания одной переменной и стандарт не определяет их порядок.

 Профиль  
                  
 
 Re: Наблюдение за префиксным инкрементом
Сообщение28.03.2022, 00:19 
Заслуженный участник
Аватара пользователя


16/07/14
9216
Цюрих
slavav в сообщении #1551211 писал(а):
Цитата из стандарта
Это из какого-то старого, то ли в 11, то ли в 17 убрали понятие sequence point и ввели sequenced-before (как раз ради многопоточности).

 Профиль  
                  
 
 Re: Наблюдение за префиксным инкрементом
Сообщение28.03.2022, 09:52 
Заблокирован


19/02/13

2388
Два момента вижу:
1) Пример имеет развлекательный характер. На практике его вряд ли стоит использовать.
2) Два компилятора с ним отлично справились, никаких трудностей у них не возникло.
mihaild в сообщении #1551208 писал(а):
Это хорошо видно с многопоточностью:

А какое отношение к стартовому примеру имеет многопоточность? Там сферическая переменная в вакууме, что в неё положили - то там и лежит, работает с ней только один поток. Справа налево согласно приоритету операций.

-- 28.03.2022, 10:20 --

slavav в сообщении #1551211 писал(а):
Стандарт запрещает выражения вида i = ... ++i ...; так как такие выражения обновляют переменную i два раза.


С технической точки зрения никаких проблем нет: загрузили в регистры процессора значение из конкретной ячейки памяти и в неё же вернули результат. Лаконично и красиво.

 Профиль  
                  
 
 Re: Наблюдение за префиксным инкрементом
Сообщение28.03.2022, 10:40 
Заслуженный участник


09/05/12
25179
Vladimir-80 в сообщении #1551219 писал(а):
Два компилятора с ним отлично справились, никаких трудностей у них не возникло.
Неопределенное поведение - это трудность не для компилятора, а программиста. Компилятор в такой ситуации вполне может что-то сделать, только вы заранее не узнаете, что именно.

 Профиль  
                  
 
 Re: Наблюдение за префиксным инкрементом
Сообщение28.03.2022, 10:45 
Заблокирован


19/02/13

2388
Интересно, почему молчит автор темы? Наверняка он, затевая беседу, имел какие-то мысли по этому поводу.

 Профиль  
                  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 34 ]  На страницу 1, 2, 3  След.

Модераторы: Karan, Toucan, PAV, maxal, Супермодераторы



Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group