2014 dxdy logo

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

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




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

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

 
 
 
 Posted automatically
Сообщение26.03.2022, 17:59 
 i  Тема перемещена из форума «Computer Science» в форум «Карантин»
по следующим причинам:

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

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

 
 
 
 Posted automatically
Сообщение26.03.2022, 19:54 
 i  Тема перемещена из форума «Карантин» в форум «Программирование»

 
 
 
 Re: Наблюдение за префиксным инкрементом
Сообщение26.03.2022, 21:41 
В 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 
Аватара пользователя
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 
Код:
i = ++i + ++i;
порождает неопределённое поведение. Это значит что результат может быть любой. Можно поизучать машинный код и порассуждать на тему какие могли бы быть результаты при той или иной интерпретации этого выражения, которое, повторюсь, не является корректным выражением на языке C++.

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


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

-- 27.03.2022, 21:31 --

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

-- 27.03.2022, 21:35 --

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

 
 
 
 Re: Наблюдение за префиксным инкрементом
Сообщение27.03.2022, 21:57 
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 
Progger в сообщении #1551202 писал(а):
переменная в выражении меняется дважды, и не понятно какое значение она должна принять


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

 
 
 
 Re: Наблюдение за префиксным инкрементом
Сообщение27.03.2022, 23:43 
Аватара пользователя
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 
Цитата из стандарта:
Цитата:
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 
Аватара пользователя
slavav в сообщении #1551211 писал(а):
Цитата из стандарта
Это из какого-то старого, то ли в 11, то ли в 17 убрали понятие sequence point и ввели sequenced-before (как раз ради многопоточности).

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

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

-- 28.03.2022, 10:20 --

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


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

 
 
 
 Re: Наблюдение за префиксным инкрементом
Сообщение28.03.2022, 10:40 
Vladimir-80 в сообщении #1551219 писал(а):
Два компилятора с ним отлично справились, никаких трудностей у них не возникло.
Неопределенное поведение - это трудность не для компилятора, а программиста. Компилятор в такой ситуации вполне может что-то сделать, только вы заранее не узнаете, что именно.

 
 
 
 Re: Наблюдение за префиксным инкрементом
Сообщение28.03.2022, 10:45 
Интересно, почему молчит автор темы? Наверняка он, затевая беседу, имел какие-то мысли по этому поводу.

 
 
 [ Сообщений: 34 ]  На страницу 1, 2, 3  След.


Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group