Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: фича компиляторв
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
Страницы: 1, 2
zhevak
На форуме программистов (здесь http://www.rsdn.ru/Forum/?mid=2481623) с пацанами сейчас подняли интересную тему. Думаю, что си-шникам тоже следует остерегаться подобного кода. В двух словах проблема состоит в том, как компилятор должен понимать оператор инкремента.



int i;
int x;

i = 5;
x = ++i + ++i;



Чему в результате будет равно х ?

Прикол в том, что MS VC++ 6.0 и VC++.NET дают результат, равный 14. Майкрософтовский C# и IAR говорят, что х = 13. А более всего удивил Watcom -- у него результат зависит от того, включена-ли оптимизация или нет... 07.gif

Я, конечно, понимаю, что код, который обсуждается, несколько далек от жизни, но все же, знать о таких прибабахах -- надо.

Вечером дома, попробую еще в CodeVision эту фичу покрутить.
vromanov
Тут все просто. По стандарту результат исполнения такого кода не определен. Тот, кто пишет такой код - сам себе злобный буратино.
Ivan_Kov
Проблема стара как "С"...
Скобки используйте и все дела.
amw
GCC 3.4.5
gcc -o x x.c
x = 14
gcc -Os -o x x.c
x = 14
gcc -O0 -o x x.c
x = 14
gcc -O1 -o x x.c
x = 14
gcc -O2 -o x x.c
x = 14
gcc -O3 -o x x.c
x = 14
zltigo
Цитата(zhevak @ May 11 2007, 14:25) *
как компилятор должен понимать оператор инкремента.

Понимать он должен правильно smile.gif В данном выражении префиксные инкременты имеют максимальные приоритеты, посему формально при тупом исполнении 14, но выражение глупозапутанное и как раз тот случай, когда оптимизаторы 'ломаются' sad.gif. Просто так писать не надо!
И насчет слова 'оптимизация' - она очень разная бывает, у меня, например, Watcom при включении в проект со вполне любовно подобранной оптимизацией без вопросов выдал 14.
A IAR честно предупредил, что не понимает такой хрени и за результат не ручается.
vromanov
не страдайте фигней.
Вот вам выдержка из стандарта С++
i = ++i + i //the behavior is unspecified
zltigo
Цитата(vromanov @ May 11 2007, 15:39) *
не страдайте фигней.
Вот вам выдержка из стандарта С++
i = ++i + i //the behavior is unspecified

Смотрим внимательно и находим одно отличие:
Код
i= ++i + i;
x= ++i + i;
vromanov
Как любителю стандартов, вам надо было не искать отличия, а пойти почитать стандарт.
Там написано, что при наличии сторонних эффектов но описанных в стандарте поведение не определено. Тут как раз имеем сторонний эффект не описанный в стандарте.
zltigo
Цитата(vromanov @ May 11 2007, 15:51) *
Тут как раз имеем сторонний эффект не описанный в стандарте.

Тут (x= ++i + i) в отличии от(i = i++ + i) его нет.
dxp
Цитата(vromanov @ May 11 2007, 19:51) *
Как любителю стандартов, вам надо было не искать отличия, а пойти почитать стандарт.
Там написано, что при наличии сторонних эффектов но описанных в стандарте поведение не определено. Тут как раз имеем сторонний эффект не описанный в стандарте.

Побочные эффекти возникают, когда объект меняется больше одного раза между точками следования. Очевидно, что в рассматриваемом выражении x= ++i + i, объект i меняется всего один раз.
Сергей Борщ
Цитата(dxp @ May 11 2007, 16:37) *
Очевидно, что в рассматриваемом выражении x= ++i + i, объект i меняется всего один раз.
А в исходном ++i + ++i?
zltigo
Цитата(Сергей Борщ @ May 11 2007, 17:22) *
А в исходном ++i + ++i?

В исходном - не хорошо c точки зрения потенциального возникновения неоднозначности sad.gif.
Ivan_Kov
Я вот смотрю, и не могу никак понять, что за нужда заставляет человека использовать подобные конструкции?
Oldring
Цитата(vromanov @ May 11 2007, 15:29) *
Тут все просто. По стандарту результат исполнения такого кода не определен. Тот, кто пишет такой код - сам себе злобный буратино.



Угу

Цитата
Between the previous and next sequence point an object shall have its stored value
modified at most once by the evaluation of an expression. Furthermore, the prior value
shall be read only to determine the value to be stored.70)


Цитата
If a ‘‘shall’’ or ‘‘shall not’’ requirement that appears outside of a constraint is violated, the
behavior is undefined.


Цитата
1 undefined behavior
behavior, upon use of a nonportable or erroneous program construct or of erroneous data,
for which this International Standard imposes no requirements
2 NOTE Possible undefined behavior ranges from ignoring the situation completely with unpredictable
results, to behaving during translation or program execution in a documented manner characteristic of the
environment (with or without the issuance of a diagnostic message), to terminating a translation or
execution (with the issuance of a diagnostic message).


В выражении ++i + ++i значение i изменяется два раза, следовательно, по стандарту результат неопределенный.

В выражении ++i + i значение i считывается два раза, один раз для вычисления нового сохраняемого значения i слева, второй раз - при вычислении правой части сложения, следовательно, по стандарту результат неопределенный.

Обратите внимание, что ключевой момент для применения правила об одном считывании - значение переменной i модифицируется в выражении.
vromanov
Удивительно, что столь очевидная вещь собрала столько сломанных копий. Есть кучу куда более прикольных особеностей в с++ smile.gif
zltigo
Цитата(Oldring @ May 11 2007, 18:37) *
В выражении ++i + i ....

Таки да! Имеет место быть неопределенность исполнения side effect от оператора ++ по отношению к операции сложения. Взгляд замылил префиксный инкремент sad.gif, который на самом деле не имеет отношения к делу.
Если вернуться к первоисточнику, то в этом конкретном случае ничего принципиально не разрешимого для компилятора нет, однако смягчения правил игры установленные стандартом в виде виде sequence point позволяют ему не разбираться с этими заморочками. Совершенно правильный подход к делу, поскольку заумные выражения усложняет жизнь компилятору и на самом деле ничего не дают програмисту.
С другой стороны никто не может запретить разбирать и более сложные конструкции в этом случае он (Watcom, например) спокойно жует и такие перлы и выдает правильное значение 14. Те компиляторы, которые не разбирают - ругаются (IAR). Те компиляторы, которые не разбирают, молчат и выдают произвольное значение - бяки smile.gif.

Цитата(vromanov @ May 11 2007, 19:08) *
Удивительно, что столь очевидная вещь собрала столько сломанных копий.

Где Вы копья увидели? А вещь не очевидная, если считать очевидным не использование такой дури в явном виде ни при каких условиях smile.gif.
defunct
Цитата
x = ++i + ++i;
cranky.gif
Это в китайских исходниках такое нашли?
zltigo
Цитата(Ivan_Kov @ May 11 2007, 14:33) *
Проблема стара как "С"...
Скобки используйте и все дела.

Скобки не определяют Sequence points и не помогут ничем.
vromanov
Цитата(zltigo @ May 11 2007, 21:48) *
Где Вы копья увидели? А вещь не очевидная, если считать очевидным не использование такой дури в явном виде ни при каких условиях smile.gif.

По всем сообществам разбирают эту строчку. И куча народа дже после правильного ответа продолжают что-то придумывать, строить какие-то предположения..
Еще и колеги на работе по аське достали этим выражением.
zltigo
Цитата(vromanov @ May 11 2007, 21:54) *
И куча народа дже после правильного ответа продолжают что-то придумывать, строить какие-то предположения..

Никаких придумок - либо компилятор должен выдать 14, либо ссылаясь на биль о правах компиляторов отказаться отвечать на этот вопрос допущения стандарта послать такое выражения подальше.
lebiga
Цитата(vromanov @ May 11 2007, 20:08) *
Удивительно, что столь очевидная вещь собрала столько сломанных копий. Есть кучу куда более прикольных особеностей в с++ smile.gif

//--------------------------------------

Когда-то полдня убил на глюк ИАРА. Код ниже работал в MCС18 (пик), а в ИАРЕ (4.30, ARM) нет.

unsigned char c;
char* cr;

выше инициализация credential;

while((c = *credential) != '\r') // Advance until \r (end of credentials)
credential++;
*credential = '\0'; // NULL char to replace \r.

//---------------------------
Пока не заменил на:

do { if(*credential=='\r') break;
credential++;
}while(1);
credential++;
*credential = '\0'; // NULL char to replace \r.

Причем игрался с оптимизациями - не помогало. Вылетало за пределы массивов.
Oldring
Цитата(zltigo @ May 11 2007, 23:03) *
Никаких придумок - либо компилятор должен выдать 14, либо ссылаясь на биль о правах компиляторов отказаться отвечать на этот вопрос допущения стандарта послать такое выражения подальше.


Почему 14?
IMHO правильное значение может быть 12, 13, но никак не 14. biggrin.gif
vromanov
Цитата(zltigo @ May 11 2007, 23:03) *
Никаких придумок - либо компилятор должен выдать 14, либо ссылаясь на биль о правах компиляторов отказаться отвечать на этот вопрос допущения стандарта послать такое выражения подальше.

Компилятор ничего не должен..
В стандарте не написано, что он должен посылать такое выражение.


Цитата(lebiga @ May 11 2007, 23:57) *
//--------------------------------------

Когда-то полдня убил на глюк ИАРА. Код ниже работал в MCС18 (пик), а в ИАРЕ (4.30, ARM) нет.

А вот это уже 100% глюк компилятора. Точнее оптимизатора.
zltigo
Цитата(vromanov @ May 11 2007, 23:02) *
Компилятор ничего не должен..
В стандарте не написано, что он должен посылать такое выражение.

Нет, если он взялся разбирать это выражение, то он должен руководствуясь общими правилами выполнения операций и их приоритетов выдать такое значение. А вот отказаться имеет право и в случае отказа естествено не должен ничего и ни кому.




Цитата(Oldring @ May 11 2007, 22:59) *
IMHO правильное значение может быть 12, 13, но никак не 14. biggrin.gif

Выполняется инкремент i, как самый приоритетный (левый или правый на Ваше усмотрение smile.gif )
Затем второй инкремент i, потом сложение, как самая неприоритетная операция.
Выполнены все три операции в порядке приоритетов. Все.
Oldring
Цитата(zltigo @ May 12 2007, 00:19) *
Нет, если он взялся разбирать это выражение, то он должен руководствуясь общими правилами выполнения операций и их приоритетов выдать такое значение. А вот отказаться имеет право и в случае отказа естествено не должен ничего и ни кому.


http://electronix.ru/forum/index.php?s=&am...st&p=249062

Цитата
Possible undefined behavior ranges from ignoring the situation completely with unpredictable
results
, to behaving during translation or program execution in a documented manner characteristic of the
environment (with or without the issuance of a diagnostic message), to terminating a translation or
execution (with the issuance of a diagnostic message).


Это цитата из стандарта. На русский переводить не нужно? Как видите, компилятор никому ничего не должен, все остальное - фантазии.

Цитата(zltigo @ May 12 2007, 00:19) *
Выполняется инкремент i, как самый приоритетный (левый или правый на Ваше усмотрение smile.gif )
Затем второй инкремент i, потом сложение, как самая неприоритетная операция.
Выполнены все три операции в порядке приоритетов. Все.


Да, именно так. Пусть аргументы '+' вычисляются справа налево wink.gif Сначала считывается значение правого i (5) прибавляем 1, записываем значение правого i. Запоминаем результат правого подвыражения (6). Затем считываем значение левого i (6), прибавляем 1, сохраняем новое значение i, запоминаем результат левого подвыражения (7). Теперь к результату правого подвыражения (6) прибавляем результат левого подвыражения (7) и в результате получаем 13.

Альтернатива - когда результаты преинкремента прописываются в переменные в конце вычисления выражения (имеют право - точка сохранения результатов однозначно не определена). В этом случае результат двух подвыражений будет 6, и их сумма будет 12.

А вот как получить 14 - не понимаю biggrin.gif
zltigo
Цитата(Oldring @ May 12 2007, 00:07) *
Это цитата из стандарта. На русский переводить не нужно? Как видите, компилятор никому ничего не должен, все остальное - фантазии.

Переводить не нужно. Может вам мой предыдущий пост на английский перевести? Или постараетесь мой русский язык понять? Повторять не вижу смысла.
Цитата
Запоминаем результат правого подвыражения (6)

Вот 'запоминаем' это и есть чистейшей воды фантазии. С какого бодуна компилятор будет заводить промежуточную переменную-дубль для запоминания? Только если у него совсем крышу снесло.
Цитата
Альтернатива - когда результаты преинкремента прописываются в переменные в конце вычисления выражения (имеют право - точка сохранения результатов однозначно не определена). В этом случае результат двух подвыражений будет 6, и их сумма будет 12.

Это или инкремент суммирование инкремент суммы с тем-же результатом - имеет право, только если это он сделает без warnig я не пойму такой поступок.
Цитата
А вот как получить 14 - не понимаю biggrin.gif

Тогда перечитайте, вдруг поймете. Если не поймете - можете поискать компилятор, который без warning выдаст результат отличный от 14. Может удастся принять, как даденность smile.gif. Вдруг найдете - сообщите, что-бы вдруг я случайно не стал пользовать небрежно писанный компилятор.
Oldring
Цитата(zltigo @ May 12 2007, 01:57) *
Переводить не нужно. Может вам мой предыдущий пост на английский перевести? Или постараетесь мой русский язык понять? Повторять не вижу смысла.


А что - переведите, будет любопытно. biggrin.gif
Вот только это все равно будет Ваше личное мнение, пусть и на английском языке, в отличие от процитированных мною требований стандарта.

Цитата(zltigo @ May 12 2007, 01:57) *
Вот 'запоминаем' это и есть чистейшей воды фантазии. С какого бодуна компилятор будет заводить промежуточную переменную-дубль для запоминания? Только если у него совсем крышу снесло.


По правилам формальной семантики выражений языка С (так как мы говорим о "правильности" выражений - мы на время забываем, что такое выражение в целом - undefined bihavior). '+' суммирует результаты своих левого и правого подвыражений. С какого бодуна результат первого подвыражения изменяется после его вычисления?

Цитата(zltigo @ May 12 2007, 01:57) *
Тогда перечитайте, вдруг поймете. Если не поймете - можете поискать компилятор, который без warning выдаст результат отличный от 14. Может удастся принять, как даденность smile.gif. Вдруг найдете - сообщите, что-бы вдруг я случайно не стал пользовать небрежно писанный компилятор.


Я-то как раз понимаю, как компилятор получает такой результат. У многих компиляторов один из промежуточных этапов компиляции - получение трехадресного кода. При этом разработчики компиляторов, помня правило, процитированное мною первым в первом моем посте, полагаются на то, что переменная i второй раз в одном и том же выражении не изменится, и в ней можно сохранить промежуточный результат подвыражения вместо заведения еще одной промежуточной переменной. Такое предположение компилятора в данном случае является ошибочным. То есть такое поведение - побочный эффект, допускаемый стандартом как undefined bihavior, но никак не "правильное" поведение компилятора. biggrin.gif

P.S. Что касается "небрежно написанных компиляторов" - есть такая русская пословица, про зеркало. Компилятор может выдавать что угодно в подобных выражениях. Делать или нет специальный анализ выражения для генерации предупреждений в таких случаях - это выбор разработчиков компиляторов, тем более, что во многих случаях (с указателями) компилятор просто не может знать, что два подвыражения модифицируют один объект. Поэтому лучше так не пишите, а если пишите - то ничего не требуйте от компилятора. Язык С, действительно, налагает довольно высокие требования на квалификацию программиста в части неиспользования некоторых конструкций.
zltigo
Цитата(Oldring @ May 12 2007, 10:23) *
Я-то как раз понимаю, как компилятор получает такой результат....

Я тоже. Только это внутреннее дело компилятора, как он обрабатывает неправильные с точки зрения стандарта (определяющего минимальные требования к компилятору) конструкции. То, что k=++i + ++i; компилятор имеет право не заморачиваясь на специальные анализы тупо на автомате разбирать и получать непредсказуемый результат это понятно. И никто никогда против этого не возражал.

То, что разработчики компиляторов имеют право не прикрываясь минимальными требования стандарта разбирать более сложные выражения, надеюсь не вызывает Вашего категорического непрятия? Впрочем, если и вызывает, то пожалуй с этим разработчики компиляторов могут не согласиться smile.gif.

Выражение k=++i + ++i; являсь совершенно диким, тем не менее поддается однозначной разборке согласно основополагающим правилам операций и их приоритетов. Про трудности компиляции такого и _правильное_ решение стандарта разрешить не заморачиваться с такой разборкой все ясно.

Цитата
Что касается "небрежно написанных компиляторов" - есть такая русская пословица, про зеркало.

Это не ко мне. Я не писал, не пишу и не буду писать подобных выражений, но тем не менее, если вдруг комилятор позволит себе не выдав предупреждение в таком выражении выдать значение отличное от 14, то это меня насторожит и приведет к некоторому недоверию к такому компилятору, поскольку он чего-то не поняв позволил себе меня не предупредить.
Цитата
Делать или нет специальный анализ выражения для генерации предупреждений в таких случаях - это выбор разработчиков компиляторов

Это их право. Только у меня тоже есть право smile.gif не использовать такие компиляторы smile.gif.
Поскольку сегодня выходной день, то я провел маленький тест используемых мною в повседневной жизни компиляторов.
OpenWatcom, GCC, Borland не возразили и выдали правильный результат.
IAR, VisualDSP - выдали предупреждение.
Все это с учетом того, что warnigs я всегда по максимуму активизирую и без оптимизации компиляцию не пользую.
Результат меня полностью удовлетворил - по поводу разборки этой дури у меня претензий к используемым мною компиляторам нет и с их разработчиками у меня коннсенсус smile.gif.

Цитата
Поэтому лучше так не пишите, а если пишете - то ничего не требуйте от компилятора.

К чему это адресное обращение? Я призывал так писать???

P.S.
Пожалуй по четвертому кругу я больше не пойду изъясняться по проблемам k=++i + ++i;
Oldring
Цитата(zltigo @ May 12 2007, 12:31) *
Выражение k=++i + ++i; являсь совершенно диким, тем не менее поддается однозначной разборке согласно основополагающим правилам операций и их приоритетов. Про трудности компиляции такого и _правильное_ решение стандарта разрешить не заморачиваться с такой разборкой все ясно.


Ну так приведите свой правильный разбор. Я свой привел, приводящий к 12 или 13. Я утверждаю, что 14 - это неправильный результат исходя из семантики операций.

Цитата(zltigo @ May 12 2007, 12:31) *
К чему это адресное обращение? Я призывал так писать???


В ответ на Ваше утверждение, что для Вас имеет принципиальное значение, какой результат выдается для этого выражения. Если я Вас неправильно понял - тогда извините.
zltigo
Цитата(Oldring @ May 12 2007, 11:45) *
Ну так приведите свой правильный разбор.

Я приводил.
Цитата
Я свой привел, приводящий к 12 или 13.

Приводили, но не с точки зрения базовых правил а с точки зрения как может быть построена разборка выражения в потрохах компилятора. Что позволяет объяснить, почему появляется неправильный результат и почему стандарт не требует разбирать такую ерунду. Но все это не имеет отношения к пользователю компилятора написавшего дурацкое, сложно разбираемое компилятором (если он за это возьмется!), хреново оптимизируемое и трудно понимаемое человеком, но вполне однозначо трактуемое выражение - "берем одну едиственную переменную, инкрементируем один раз, инкрементируем второй раз, прибавляем к ней ее-же значение.
Цитата
В ответ на Ваше утверждение, что для Вас имеет принципиальное значение, какой результат выдается для этого выражения. Если я Вас неправильно понял - тогда извините.

Про "принципиальное значение" я ни сном ни духом не поминал. Повторяю - выдаваемый результат лично для меня имеет некоторое (а отнюдь не принципиальное) значение только в плане оценки компилятора и только в том единственном случае, когда он не выдав предупреждения выдал результат отличный от 14. В проведенных тестах я даже не смотрел на результат у компиляторов которые выдали warning-и. Во настолько он для меня имеет "принципиальное значение" smile.gif
Извиняю.
Oldring
Цитата(zltigo @ May 12 2007, 13:26) *
Я приводил.


Не вижу именно разбора выражения. По порядку, что в каком порядке должно вычисляться и почему?
zltigo
Цитата(Oldring @ May 12 2007, 12:34) *
Не вижу..

Жаль.
Oldring
Цитата(zltigo @ May 12 2007, 13:36) *
Жаль.


То есть ссылку на свой пост, про который Вы утверждаете, что там описали формальный разбор выражения, Вы привести не можете?

Цитата(zltigo @ May 12 2007, 13:26) *
Приводили, но не с точки зрения базовых правил а с точки зрения как может быть построена разборка выражения в потрохах компилятора. Что позволяет объяснить, почему появляется неправильный результат и почему стандарт не требует разбирать такую ерунду. Но все это не имеет отношения к пользователю компилятора написавшего дурацкое, сложно разбираемое компилятором (если он за это возьмется!), хреново оптимизируемое и трудно понимаемое человеком, но вполне однозначо трактуемое выражение - "берем одну едиственную переменную, инкрементируем один раз, инкрементируем второй раз, прибавляем к ней ее-же значение.


Нет, грамматические и семантические правила - это часть стандарта языка, а не реализации конкретного компилятора. Я ссылался на грамматические правила и на семантику операций. Выражение x = ++i + ++i; должно быть грамматически разобрано следующим образом (запись дерева - стандартная префиксная)

Код
assign(
    addr( 'x' ),
    sum(
        assign(
            addr( 'i' ),
            add(
                value( addr( 'i' ) ),
                1
            )
        ),
        assign(
            addr( 'i' ),
            add(
                value( addr( 'i' ) ),
                1
            )
        )
    )
)


Как при аккуратном исполнении этого дерева разбора в соответствии с семантическими правилами языка можно получить 14 - не понимаю.

P.S. Если придираться - в этом дереве уже расрыта операция ++i как эквивалентная (i+=1).
vetal
По моему все очень просто, т.к. код архитектурно зависимый.
Даже считая по дереву:
Код
1. *i=*i+1;[6]
2. *i=*i+1;[7]
3. x=i+i;[14]

С другой стороны, результат в java(как в стековой архитектуре).
Код
1. i=i+1[6]
2. push
3. i=i+1[7]
4. push
5. add[13]

Все прозрачно)))
Oldring
Цитата(vetal @ May 12 2007, 14:16) *
3. x=i+i;[14][/code]
Все прозрачно)))


Так вот откуда взялась эта третья операция - x = i + i?
Как она получена из дерева разбора выражения?

P.S.

++i эквивалентно i += 1.

Цитата
An assignment expression has the value of the left operand after the assignment, but is not an
lvalue.
vetal
Цитата
Так вот откуда взялась эта третья операция - x = i + i?
Как она получена из дерева разбора выражения?


В дереве ясно написано x=add {++i,++i}, т.е. сначала выполняются все преинкрементом, а затем выполняется операция сложения. Т.к. операции преинкремента выполняются с одной ячейкой памяти - происходит увеличение ячейки на 2. Операция сложения складывает 2 ячейки памяти с адресом &i, т.е. как и написано в коде. Это вполне справедливо при рассмотрении x86 как машины с памятью, которой она и является)))

Я не могу спрогнозировать результат только на регистровой машине(классической или как при представлении x86 в регистровом эквиваленте), т.к. неопределенность будет зависеть от результата оптимизации. На регистровой машине будут справедливы 2 подхода : -неоптимизированный (по первичному графу) получится как в x86, т.к. сначала выполнятся все "пре" операции и получится результат как в машине с памятью;
-оптимизированный, когда все будет делаться на лету и максимальным использованием регистров код может выглядеть как
Код
ldw   r1, i
add   r1,r1,1
stw   i, r1; можно исключить
mov  r2,r1
add   r2,r2,1
stw    i,r2
add   r3,r1,r2
Oldring
Цитата(vetal @ May 12 2007, 14:42) *
В дереве ясно написано x=add {++i,++i}, т.е. сначала выполняются все преинкрементом, а затем выполняется операция сложения. Т.к. операции преинкремента выполняются с одной ячейкой памяти - происходит увеличение ячейки на 2. Операция сложения складывает 2 ячейки памяти с адресом &i, т.е. как и написано в коде. Это вполне справедливо при рассмотрении x86 как машины с памятью, которой она и является)))


Не совсем - в дереве ясно написано, что нужно сложить результат первого подвыражения и результат второго подвыражения. Если бы результат подвыражений был lvalue - тогда при вычислении сложения нужно было бы взять у lvalue (неформально - ссылки на объект) значение объекта, и тогда можно было бы прочитать из переменной i результат, отличный от приписанного в i в подвыражении, если бы преобразование lvalue в значение выполнялось после вычисления обоих подвыражений как преобразование типа для операции '+'. Но так как результат вычисления подвыражения lvalue не является - мы не имеем права считывать его обратно из i и обязаны использовать именно то значение, которое было вычислено соответсвующим подвыражением. Одно из подвыражений всегда даст 6, в каком бы порядке ни выполнять операции.

Что происходит в реальных компиляторах - вполне понятно, это не вопрос. Речь шла про "правильность", причем, после закрывания глаз на то, что такое выражение в целом имеет undefined bihavior.
vetal
Потому и "undefined bihavior", что по моему мнению оператор + складывает 2 ячейки памяти с адресом I, а по вашему он заводит дополнительную переменную. Тут всё и кроется))
zltigo
Цитата(vetal @ May 12 2007, 13:42) *
Это вполне справедливо при рассмотрении x86 как машины с памятью, которой она и является)))

Вся эта бодяга действительно отдана на откуп компилятору в зависимости от платформы. Но что интересно - в результате реального эксперимента все компиляторы для x86 платформы выдали тот самый результат, а под ARM и BF выдали warning - не смогли на штатном механизме обеспечить?
Интересный такой нюанс. Надо будет GNU на не на x86 платформе спытать.
Oldring
Цитата(vetal @ May 12 2007, 15:11) *
Потому и "undefined bihavior", что по моему мнению оператор + складывает 2 ячейки памяти с адресом I, а по вашему он заводит дополнительную переменную. Тут всё и кроется))


undefined bihavior - потому что часто зависит, например, от порядка вычисления подвыражений, да и для свободы оптимизатора полезно, когда побочные эффекты можно вычислять в произвольном порядке.

Ваше мнение о том, "что оператор + складывает 2 ячейки памяти с адресом i" противоречит утверждению из стандарта, что результат вычисления ++i не есть lvalue. Так что такую интерпретацию принять не могу как "правильную" smile.gif


Цитата(zltigo @ May 12 2007, 15:48) *
Вся эта бодяга действительно отдана на откуп компилятору в зависимости от платформы. Но что интересно - в результате реального эксперимента все компиляторы для x86 платформы выдали тот самый результат, а под ARM и BF выдали warning - не смогли на штатном механизме обеспечить?
Интересный такой нюанс. Надо будет GNU на не на x86 платформе спытать.


arm-elf-gcc версии 4.1.1 выдает 13. cool.gif
bus16
Посмотрите полученный ассемблерный код на своей машине и всё станет ясно: на ARM без использования стека (ес-но) i=13, на х86 без стека i=14, со стеком i=13.
Вообще беспредметный спор о тёмных сторонах компилляторов, ИМХО.
Oldring
Цитата(Niko1a$ @ May 12 2007, 16:42) *
Вообще беспредметный спор о тёмных сторонах компилляторов, ИМХО.


Безусловно.
Спор был о том, можно ли считать результат 14 "правильным", называя все остальное - "неправильным".
zltigo
Цитата(Oldring @ May 12 2007, 15:23) *
arm-elf-gcc версии 4.1.1 выдает 13. cool.gif

Молча?
vetal
nios2-elf-gcc выдает без оптимизации 13, с оптимизацией - 14.
Хотя оптимизированный код выглядит совсем оптимизированным "movi R2, 14" )))
И говорит что так делать нельзя.
Oldring
Цитата(zltigo @ May 12 2007, 17:05) *
Молча?


По умолчанию - да.

В программе, приведенной ниже, с ключем -Wsequence-point выдается предупреждение "warning: operation on 'i' may be undefined" внутри main(), но не внутри f(). В обоих случаях - 13.

Код
int f( int* p1, int* p2 )
{
    return ++(*p1) + ++(*p2);
}

int main( void )
{
    int i;
    int x1, x2;

    i = 5;
    x1 = ++i + ++i;

    i = 5;
    x2 = f( &i, &i );

    return x2;
}


P.S. С включенной оптимизацией -O3 main своится к возврату 13.
zltigo
Цитата(Oldring @ May 12 2007, 16:14) *
В программе, приведенной ниже, с ключем -Wsequence-point выдается предупреждение "warning: operation on 'i' may be undefined" внутри main(), но не внутри f(). В обоих случаях - 13.

Ну про f() это естественно правильно - по другому было-бы крайне странно. С main() выдал warning расписавшись в непонятках - тоже каой-же после этого спрос.

Зксперименировал для начала с OpenWatcom...
Цитата
P.S. С включенной оптимизацией -O3 main своится к возврату 13.

Аналогично. C приличными оптимизациями все сводится к smile.gif
Код
    push      0000000eH
    push      offset FLAT:L$402
    call      near ptr FLAT:printf_
    add       esp,00000008H
    ret

Пришлось извне передавать.
Код
void dummy( int i )
{
int k = ++i + ++i;
    printf( "k=%i\r", k );
}

Это вариант с разнообразными оптимизаторами:
Код
    add       eax,00000002H
    add       eax,eax
    push      eax
    push      offset FLAT:L$402
    call      near ptr FLAT:printf_
    add       esp,00000008H
    ret

и вообще без оптимизации:
Код
    push      00000030H
    call      near ptr FLAT:__CHK
    push      ebx
    push      ecx
    push      edx
    push      esi
    push      edi
    push      ebp
    mov       ebp,esp
    sub       esp,0000000cH
    mov       dword ptr -0cH[ebp],eax
    inc       dword ptr -0cH[ebp]
    inc       dword ptr -0cH[ebp]
    mov       eax,dword ptr -0cH[ebp]
    add       eax,dword ptr -0cH[ebp]
    mov       dword ptr -4H[ebp],eax
    push      dword ptr -4H[ebp]
    mov       eax,offset FLAT:L$450
    push      eax
    call      near ptr FLAT:printf_
    add       esp,00000008H
    mov       esp,ebp
    pop       ebp
    pop       edi
    pop       esi
    pop       edx
    pop       ecx
    pop       ebx
    ret

Но при любых уровнях оптимизации "неизменно превосходный результат". warnin-ов нет. Причем, что действительно интересно даже в вырожденном случае с тупым возвратом значения , где уже все заранее решено - 14.



Цитата(vetal @ May 12 2007, 16:09) *
И говорит что так делать нельзя.

В обоих случаях оптимизации говорит нельзя?
Oldring
Цитата(zltigo @ May 12 2007, 18:14) *
Но при любых уровнях оптимизации "неизменно превосходный результат". warnin-ов нет. Причем, что действительно интересно даже в вырожденном случае с тупым возвратом значения , где уже все заранее решено - 14.


В любом случае, это не есть доказательство "правильности" значения 14.

Цитата(zltigo @ May 12 2007, 18:14) *
Ну про f() это естественно правильно - по другому было-бы крайне странно. С main() выдал warning расписавшись в непонятках - тоже каой-же после этого спрос.


gcc на x86 неизменно возвращает 14. в том числе, из f() smile.gif
zltigo
Цитата(Oldring @ May 12 2007, 17:38) *
В любом случае, это не есть доказательство "правильности" значения 14.

Ну скажем так, это некий результат полученный на нескольких компиляторах, без сопутствующего warning (типа не сомневались в результате?). Все другие компиляторы выдающие отличный результат неизменно генерили при этом warning. Как это можно истолковать?
А с учетом того, что авторы компиляторов разбираюся в этом скорее всего лучше, чем все здесь собравшиеся вместе взятые?
Цитата
gcc на x86 неизменно возвращает 14. в том числе, из f() smile.gif

Я мельком глянул на f() - естественно ошибся sad.gif.
vetal
Цитата
В обоих случаях оптимизации говорит нельзя?

Да. Для O2 и O0
Oldring
Цитата(zltigo @ May 12 2007, 18:51) *
Ну скажем так, это некий результат полученный на нескольких компиляторах, без сопутствующего warning (типа не сомневались в результате?). Все другие компиляторы выдающие отличный результат неизменно генерили при этом warning. Как это можно истолковать?
А с учетом того, что авторы компиляторов разбираюся в этом скорее всего лучше, чем все здесь собравшиеся вместе взятые?


Как истолковать? Как невыдачу предупреждения на undefined bihavior код. biggrin.gif

То, что "А с учетом того, что авторы компиляторов разбираюся в этом скорее всего лучше, чем все здесь собравшиеся вместе взятые" - утверждение просто смешное. Компилятор - не такая уж непостижимая для понимания программа. Очень много народу писало разнообразные компиляторы - ну разве что не такие оптимизирующие.

Вы никогда не читали учебники по устройству компиляторов? Вот, например, из недавних был издан на русском языке в 2001 году. "Компиляторы. Принципы, технологии, инструменты". Ахо, Сети, Ульман. Авторы, кстати - классики по теории компиляции. Очень рекомендую.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.