int t = 10;
t = ++t + ++t;
t = 24
Но:
int t = 10;
t = –t – –t;
t = 0
Объясните, в чем разница сложения и вычитания?
int t = 10;
t = ++t + ++t;
t = 24
Но:
int t = 10;
t = –t – –t;
t = 0
Объясните, в чем разница сложения и вычитания?
Клуб программистов работает уже ой-ой-ой сколько, а если поточнее, то с 2007 года.
21 августа 2008 в 9:01
gcc -Wsequence-point …
избавляемся от неопределенности
20 августа 2008 в 14:04
#44
Роман Воробец 20 авг 2008 в 14:46
ОФФТОП: Не только. Также определен порядок вычислений для операций && и ||
20 августа 2008 в 13:00
Роман Воробец, верно. но тут как раз речь о приоритетах операций =)
20 августа 2008 в 12:04
М-да, сколько безграмотных
Порядок вычисления выражений, если дело не касается приоритетов операций, определён только при использовании оператора ",".
Вот, к примеру:
foo(shared_ptr<bar>(new bar(x)), shared_ptr<bar>(new bar(y)))
где здесь ошибка?
20 августа 2008 в 12:04
2 Ростислав.
Ага. Согласен. Должен. И если он считывает значение переменной только один раз, то должен учесть такой эффект, и самостоятельно декрементировать результат до значения 23, которое и есть правильное, ибо получается в образцовом варианте (т.е. в варианте без оптимизации).
20 августа 2008 в 12:03
>Еще как подлежит, вообще volatile не имеет отношения к оптимизации никакого.
Имеет. volatile значит, что переменная может быть изменена где-то еще (в другом потоке/аппаратно/итп), отсюда следует, что ее следует считывать заново каждый раз, когда нужно ее значение, то есть нельзя использовать оптимизацию, когда используется ранее считанное значение, если оно не изменялось. (ну и наоборот: при каждом присваивании нужно ее новое значение в нее записывать. то есть если i=2; i=3; то нельзя это оптимизировать и первое присваивание выкинуть, ибо volatile).
20 августа 2008 в 12:03
Да, неправ, согласен, volatile действительно влияет именно на отключение "константных" предвычислений.
Правда в данном случае никакой речи о подобных оптимизациях нет, т.к. выражение считается в одном потоке, и компилятор, уж наверное, должен знать про все side-effects.
20 августа 2008 в 12:02
#39
>В случае с инкрементами/декрементами – сначала эти самые инкременты/декременты, потом сложение.
Я об этом же.
>Суть в том, что при включенной оптимизации значение переменной, которая входит дважды в выражение, считывается из нее только один раз
какой оптимизации? не припомню чтобы какая-нибудь комбинация параметров оптимизации в VS приводила к другому результату вычислений.
>Если же переменная volatile, то оптимизации она не подлежит
Еще как подлежит, вообще volatile не имеет отношения к оптимизации никакого.
Вообще не понимаю какие могут быть сомнения, все выражения вычисляются последовательно, значения берутся с учетом всех side effects, операции требуют вычичления своих аргументов. если gcc считает по-другому то это его косяки.
20 августа 2008 в 11:05
а оптимизация на результат влияет, например, если забыть объявить переменную volatile там, где это нужно.
20 августа 2008 в 11:05
#36
Ростислав Чутков 20 авг 2008 в 13:38
,
выражение вычисляется в порядке приоритета операций. в твоем случае – сначала умножение, затем сложение.
В случае с инкрементами/декрементами – сначала эти самые инкременты/декременты, потом сложение.
Суть в том, что при включенной оптимизации значение переменной, которая входит дважды в выражение, считывается из нее только один раз, и в данном случае это происходит после обоих инкрементов, получается 24. Если же переменная volatile, то оптимизации она не подлежит, и ее значение берется каждый раз заново. То есть: вычислили левый операнд, в процессе этого один раз ее инкрементировали; запомнили ее значение (11); вычислили правый операнд, запомнили его (12); сложили – получается 23.
20 августа 2008 в 11:05
забавно.
в D сделано очень просто: It is an error to depend on order of evaluation when it is not specified. (и приводится список, что и в какой последовательности вычисляется)
20 августа 2008 в 11:04
тут тоже переменная меняется во время вычисления.
20 августа 2008 в 11:03
Почему бы не усомниться тогда что
5*3 + 3*1 считается не как 5 * (3 + 3) * 1? тут такая же ситуация.
в t = ++t + ++t; мы имеем справа две ветки операции плюс, каждая из которых содержит инкремент. для того чтобы вычислить значение выражения, нужно получить значение обоих веток ДО применения операции, соответственно оба инкремента должны выполниться ДО операции плюс, и результат 24 абсолютно верный и логичный.
Вообще тот кто ставил вопрос наверное имел в виду
t = t++ + t++;
вот тут результат немного более интересный.
20 августа 2008 в 11:00
Разберу на примере GCC
23 будет, если t объявлено как volatile. тогда все оптимизации с этой переменной отключаются, и каждый раз при вычислении выражения берется ее текущее значение (первый раз 11, второй раз 12).
Если нет, то вследствие оптимизации значение переменной берется только один раз, после обоих инкрементов.
20 августа 2008 в 11:00
Оптимизация, по определению, не должна влиять на результат. Она должна влиять на скорость и ресурсные требования. volatile ── для тех случаев, когда переменная может меняться где-то параллельно, например, в другом процессе/потоке/нити. А тут имеется в виду единственный вычислительный поток.
20 августа 2008 в 10:04
Всё равно 23 должно быть. Даже если функция.
20 августа 2008 в 10:00
Если оператор — это функция, а операнды — это аргументы, то вариант с 24 справедлив:
++t + ++t := sum(++t, ++t)
20 августа 2008 в 8:00
Всяко 3 результат. Инкремент ведь раньше выполнится, нет?
Значение постинкремента =1, хотя t уже =2.
Затем к t(=2) прибавляется 1, и результат t=3
20 августа 2008 в 6:05
Ребят, да вы че? Ни разу чтоли не сталкивались с такими операциями?
есть еще более простой, наверное даже самый простой пример:
int t= 1;
t += t++;
Чему будет равен результат? Этого вам никто не скажет, потому как все зависит от компилятора. Почитайте статьи на тему "Точки следования" и "Undefined Behavior" в них как раз и рассказывается про такие интересные случаи.
одна из статей:
//alenacpp.blogspot.com/2005/11/sequence-points...
И совет на будущее: никогда не пишите так в своих программах
20 августа 2008 в 6:00
Логичнее вариант 1:
1. ++t; // t == 11 и (++t) == 11
2. ++t; // t == 12 и второй (++t) = 12
3. ++t + ++t; // 11 + 12 = 23
Вариант 2 вообще полная чушь.
С вариантом 3 ещё можно смириться. А вообще, не очень хорошо, что стандарт допускает такие разночтения.
20 августа 2008 в 6:00
Во! Согласен с Павлом Scavenger Потаповым, всё именно так, как думаю я.
20 августа 2008 в 2:05
2Вячеслав Kovyl Ковалёв
Здравая логика говорит о том, что должно быть 23 (все-таки результаты двух последовательных инкрементов должны отличаться) А на практике 24 получается в силу того, что t записывается в один регистр процессора и дважды в нем инкрементируется, после чего складывается.
20 августа 2008 в 0:03
>> Насколько мне известно, стандарт в данном случае не указывает
>> какой-то конкретный порядое вычисления, и "правильны" несколько
>> результатов.
Это еще называют Undefined Behaviour
20 августа 2008 в 0:00
Эту задачку для разминки мозга я отношу к категории "перлизмы". Операторы @++ и @– обладают нечеловечески дурацким поведением. Опытные люди привыкли, а неопытные взрывают себе мозги.
19 августа 2008 в 23:03
Безусловно, нужно ссылаться на стандарт, но еще существует здравая логика. Правилен все-таки третий вариант…
19 августа 2008 в 23:03
имхо, лучше так не писать) кроссплатформенность пропадает))
19 августа 2008 в 21:02
ржунимагу)))
19 августа 2008 в 20:05
Результат таких выражений зависит от реализации в компиляторе. Оптимизация, однако
19 августа 2008 в 20:00
Листал с коллегой по работе последнюю редакцию стандарта – на эту тему не нашли ни слова. Так что дается на откуп разработчику компилятора =)
19 августа 2008 в 18:04
3.4.5
19 августа 2008 в 18:02
>> GCC выдаёт 24
Хм… какая версия? Я вроде помню, что он работает по первому сценарию.
Кстати, знает ли кто, как оно в стандарте определено (и определено ли вообще)?
19 августа 2008 в 18:02
О правильности можно говорить только ссылаясь на соответствующую секцию стандарта. Насколько мне известно, стандарт в данном случае не указывает какой-то конкретный порядое вычисления, и "правильны" несколько результатов.
19 августа 2008 в 18:01
GCC выдаёт 24
19 августа 2008 в 18:01
Я считаю вариант 3 наиболее правильным
19 августа 2008 в 18:00
приоритет-то у декремента выше:
t = –t – –t;
004113C5moveax,dword ptr [t]
004113C8subeax,1
004113CBmovdword ptr [t],eax
004113CEmovecx,dword ptr [t]
004113D1subecx,1
004113D4movdword ptr [t],ecx
004113D7movedx,dword ptr [t]
004113DAsubedx,dword ptr [t]
004113DDmovdword ptr [t],edx
19 августа 2008 в 18:00
Дима, выполнятся может так по разному. Например, t = ++t + ++t
Вариант 1:
1. ++t; // t == 11 и (++t) == 11
2. ++t; // t == 12 и второй (++t) = 12
3. ++t + ++t; // 11 + 12 = 23
Вариант 2:
1. ++t; // t == 11 и оба (++t) == 11
2. ++t + ++t; // 11 + 11 = 22
Вариант 3:
1. ++t; // t == 11
2. ++t; // t == 12
3. ++t + ++t; // 12 + 12 = 24
Вариант 3 мне не попадался, но 1 и 2 второй существуют в примерно равных пропорциях.
19 августа 2008 в 17:05
ыыы) я знал, что здесь есть подвох.
19 августа 2008 в 17:05
Уже подумалось : может забыл чего… Потому что как не считал (руками) получается 23 и 1
19 августа 2008 в 17:04
Кстати, а откуда именно такие результаты выполнения кода? Посчитал – получилось 23 и 1 соответственно. C# показал то же самое.
19 августа 2008 в 17:01
кстати, экспериментально доказано, что результат такого рода операция зависит от компилятора.
19 августа 2008 в 16:04
int t = 10;
t = t+++t;
Чему равно t и почему?
19 августа 2008 в 16:04
Нечто подобное видал в книжки Страуструпа "Язык C++"
19 августа 2008 в 16:01
О нет, n – n = 0 ?! Ужас, страшно жить
19 августа 2008 в 16:01
Жека jkff Кирпичев, спасибо. рабочий день заканчивается)
19 августа 2008 в 16:00
странный вопрос.
19 августа 2008 в 16:00
ни в чем оО
где ты тут разницу увидел (кроме чисто идеологической разницы м/у плюсом и минусом, конечно ?
19 августа 2008 в 16:00
В первом случае выполнилось
++t; ++t; t = t+t;
Во втором случае
–t; –t; t = t-t;
Никакой разницы нет.