PaCHER Например мне непонятно почему такие как ты считают, что asm-листинг может быть каким-то аргументом.
_DEN_ "Что не запрещено- можно" Компилятор вправе делать, что угодно, если это не противоречит требованиям стандарта. IMHO. Здесь я тоже толком не понимаю... Подумаю.
green ну в стандарте например напрямую не сказано что чтение из указателя со значением 897435453 это UB Есть более общая оговорка - чтение из невалидного указателя это UB. Напрямую может быть и не сказано, что вычисление аргумента должно быть атомарно.
_DEN_ Я не встречал в стандарте более общего утверждения, из которого бы следовало, как частный случай, что вычисление аргумента должно быть атомарно. Посему претензий к компилятору не имею. Более того, мне нравится агрессивная, изобретательная оптимизация VC - не в пример другим компиляторам, включая Intel.
_DEN_ Не понимаю, какое отношение имеет оптимизация к кросплатформенности... Или ты намекаешь, что VC оптимизирует в ущерб соблюдению стандарта ? Можешь привести пример ? Согласен, что VC поддерживает стандарт не полностью, но, AFAIK, вовсе не из-за ошибок оптимизатора. Кроме того, не поддерживаются совсем уж экзотические фичи, без которых вполне можно обойтись.
В Стандарте (если я правильно перевел это заумное предложение ) написано, что если переменная в одном выражении изменяется дважды, то результат уже неопределён. Единственное исключение, когда результат точно определён, это при применении запятой-оператора. Насчёт цитируемого примера. Видимо, это всего лишь демонстрация правила: раз переменная изменяется дважды, то результат не определён. Хотя на всех современных компиляторах он окажется правильным (ИМХО). Да наверное, и не получится разумно описать все возможные варианты, когда выражение корректно, а когда нет. Проще все объявить неопределёнными На всякий случай приведу весь абзац из Стандарта:
i = 7, i++, i++; // i becomes 9 эээээээээээээээээээээ............... Почему тогда return 1, 2, 3, 4, 5; // вернет 5 ???
_DEN_ А что здесь странного? Для оператора comma, как и && и || порядок вычисления аргументов определён Стандартом.
green Эти два примера противорячат друг другу. i = 7, i++, i++; // i becomes 9 говорит о том, что происходит следующее: ((i = 7), i++), i++); return 1, 2, 3, 4, 5; // вернет 5 говорит о том, что это эквивалент return ((((1, 2), 3), 4), 5); А не (((((return 1), 2), 3), 4), 5);
_DEN_ приоритет у оператора присваивания выше, чем у оператора комма. А у "оператора" return - ниже, он хавает все до ;
у return вообще нет никакого приоритета if, else, goto, return итд называются операторами, так же, как и +, -, *, / ... но не надо их путать CodeTao точно. поясняю - стандарт оставляет разработчикам компиляторов широкие возможности для оптимизации, стараясь по-возможности не ограничивать их правилами, определяющими последовательность выполнения операций. в частности, он оставляет неопределенными результаты выражений, в которых какие-либо переменные между двумя "sequence points" изменяются больше, чем один раз ("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 + 1; неопределен. не из-за каких-то особенностей опреаторов, а просто из принципа другое дело, что я тоже с трудом представляю себе оптимизацию, при которой результат (с учетом того, что преинкремент дает ссылочное значение) будет отличаться от <изначальное i>+2. вторая вещь, которую следует понимать, это примечание к цитате, которую привел maxdiver: так вот, слдует помнить, что операторы не "должны выполняться в порядке их следования". приоритет операций, так же, так и правила ассоциативности, задает только правила грамматического разбора предложений: a+b*c понимается именно как a+(b*c), а не (a+b)*c, и только. оно не предписывает выполнять сначала умножение, и, затем, сложение. если b*c уже вычислялось раньше и значения операндов с тех поря явно не изменялись (в частности, не использовалось взятите их адресов с передачей в какую-нибудь функцию), компилятор может запомнить старый результат и использовать его в этом выражении (исключение составляют случаи, когда b или c объявлены как volatile). точно так же, выражение (a+b)*c говорит только о том, что что математически результат должен соответствовать сначала сложению, а затем ужножению, но не запрещает компилятору вычислять это выражение как-нибудь вот так: a*c+b*c естественно, все вышесказанное относится исключительно к базовым типам, то есть, если в предыдущем примере a, b и c являлись бы объектами, то выражение (a+b)*c однозначно превратится в вызвы: tmp= operator+(a, b) result= operator*(tmp, c) теперь что касается топика: преинкрементный ++ возвращает l-value то есть вызов func(++i, ++i) фактически можно рассматривать так: ++i; ++i; func(i, i); или так: ++i; func(i,<еще раз ++i;> i); я не знаю точно, требует ли стандарт от компилятора учитывать в данном случае, что i изменяется дважды, то есть обязан ли результат быть равен именно 4, а не 3. насколько я понимаю - нет, стандарт не определяет поведение компилятора в данном случае кстати, фактически запись func(++i, ++i) должна быть эквивалентна func(i+=1, i+=1);
Проверил этот код в C# 2.0: Код (Text): using System; using System.Collections.Generic; using System.Text; namespace plusplus { class Program { static int func(int a, int b) { return a + b; } static void Main(string[] args) { int i = 0; int x = func(++i, ++i); Console.WriteLine(x); Console.ReadKey(true); } } } Возвращает "3"
green Это вроде как все объясняет. Кроме того, почему все же i = ++i + 1 это UB. Огромный пост Nouzui к сожалению ничего не прояснил. т.к. свобода для оптимизации не должна противоречить стандарту, а если приведенный пример это UB, то от стандарта необходимо отойти. Другого способа я пока не вижу.