Приоритет операторов в C++
От: RussianFellow Россия http://russianfellow.livejournal.com
Дата: 23.01.17 06:54
Оценка:
Я сейчас читаю про приоритет операторов в C++ :

https://ru.wikipedia.org/wiki/%D0%9E%D0%BF%D0%B5%D1%80%D0%B0%D1%82%D0%BE%D1%80%D1%8B_%D0%B2_C_%D0%B8_C%2B%2B#.D0.9F.D1.80.D0.B8.D0.BE.D1.80.D0.B8.D1.82.D0.B5.D1.82.D1.8B_.D0.BE.D0.BF.D0.B5.D1.80.D0.B0.D1.82.D0.BE.D1.80.D0.BE.D0.B2

Мне там не всё понятно. В частности, мне не понятно, что такое левоассоциативность и правоассоциативность операторов в C++.
Не могли бы Вы, уважаемые коллеги, объяснить мне получше, что это такое? А заодно и рассказать понятно про приоритет операторов в C++.
Желательно также привести сложные выражения с операторами и объяснить, что они означают.

Я сейчас читаю Страуструпа, но мне хотелось бы прочитать подробно, понятно, популярно про приоритет операторов в C++.
1613 г. = 2024 г.
Re: Приоритет операторов в C++
От: RussianFellow Россия http://russianfellow.livejournal.com
Дата: 23.01.17 06:59
Оценка:
P.S. Спрашиваю так потому, что я привык писать сложные выражения в C++ со скобками. А если писать без скобок?
1613 г. = 2024 г.
Re[2]: Приоритет операторов в C++
От: Andrew.W Worobow https://github.com/Worobow
Дата: 23.01.17 08:15
Оценка: +2
Здравствуйте, RussianFellow, Вы писали:

RF>P.S. Спрашиваю так потому, что я привык писать сложные выражения в C++ со скобками. А если писать без скобок?


лучше все равно всегда писать со скобками за редким-редким исключением.
Не все кто уехал, предал Россию.
Re: Приоритет операторов в C++
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 23.01.17 08:23
Оценка: 4 (1) +1
Здравствуйте, RussianFellow, Вы писали:

RF>Мне там не всё понятно. В частности, мне не понятно, что такое левоассоциативность и правоассоциативность операторов в C++.


Это как оно расставит скобки в цепочке одинаковых (или равноприоритетных) операторов, если скобок нет.

a+b+c интерпретируется (a+b)+c, а не a+(b+c) (для большинства случаев пофиг)

a-b-c — как (a-b)-c, а не a-(b-c) (а вот тут уже не пофиг, результат совсем другой)

это всё были левоассоциативные.

А вот пример правоассоциативного — возведение в степень — почти везде

в Фортране a**b**c понимается как a**(b**c), а не как (a**b)**c, потому что последнее равно a**(b*c), и смысла в такой ассоциативности — никакого.

В Си a=b=c понимается не как (a=b)=c, а как a=(b=c). Первый вариант не имеет смысла. Второй — имеет.

RF>Не могли бы Вы, уважаемые коллеги, объяснить мне получше, что это такое? А заодно и рассказать понятно про приоритет операторов в C++.

RF>Желательно также привести сложные выражения с операторами и объяснить, что они означают.

А что они могут означать?

RF>Я сейчас читаю Страуструпа, но мне хотелось бы прочитать подробно, понятно, популярно про приоритет операторов в C++.


Почему не начать с более простой книги? Страуструп явно писал, что "не хочет оскорблять профессиональных программистов подробным разжёвыванием".
The God is real, unless declared integer.
Отредактировано 23.01.2017 8:23 netch80 . Предыдущая версия .
Re: Приоритет операторов в C++
От: RussianFellow Россия http://russianfellow.livejournal.com
Дата: 23.01.17 09:54
Оценка:
Ну, например, что означают выражения:

++*p1 = ++*p2;


*++p1 = *++p2;


*p1++ = *p2++;


И чем выражение
for (i=0; i<10; i++);


отличается от выражения
for (i=0; i<10; ++i);


?
1613 г. = 2024 г.
Re[2]: Приоритет операторов в C++
От: Chorkov Россия  
Дата: 23.01.17 10:37
Оценка:
Здравствуйте, RussianFellow, Вы писали:

RF>Ну, например, что означают выражения:


(++(*p1)) = (++(*p2));


(*(++p1)) = (*(++p2);


(*(p1++)) = (*(p2++));


RF>И чем выражение

RF>
RF>for (i=0; i<10; i++);
RF>


RF> отличается от выражения

RF>
RF>for (i=0; i<10; ++i);
RF>


RF>?


С точки зрения наблюдаемого поведения — ничем. Если иначе — значит перегрузку операторов ++, для того чем является i, писал диверсант.
Для сложных типов данных, например итераторы контейнеров, префиксный оператор (++i) написать проще чем суфиксный (i++), и обычно он работает быстрее (ненужна дополнительная переменная).
И этот пример уже не имеет отношения к приоритетам операторов.
Re[2]: Приоритет операторов в C++
От: SkyKnight Швейцария https://github.com/dmitrigrigoriev/
Дата: 23.01.17 12:52
Оценка: +1
Здравствуйте, RussianFellow, Вы писали:

RF>P.S. Спрашиваю так потому, что я привык писать сложные выражения в C++ со скобками. А если писать без скобок?

Всегда лучше писать со скобками, иначе потом или читаемость будет хреновая или же закрадется бага, которую будешь искать пол дня.
github.com/dmitrigrigoriev/
Re[2]: Приоритет операторов в C++
От: Pzz Россия https://github.com/alexpevzner
Дата: 23.01.17 13:10
Оценка: +2
Здравствуйте, RussianFellow, Вы писали:

RF>P.S. Спрашиваю так потому, что я привык писать сложные выражения в C++ со скобками. А если писать без скобок?


Открою секрет. Все помнят, что умножение имеет приоритет перед сложением, но более сложных правил не помнит никто. Поэтому все пишут сложные выражения со скобками.
Re[2]: Приоритет операторов в C++
От: Pzz Россия https://github.com/alexpevzner
Дата: 23.01.17 13:11
Оценка:
Здравствуйте, RussianFellow, Вы писали:

RF>Ну, например, что означают выражения:


RF>
RF>++*p1 = ++*p2;
RF>


Зачем обычному человеку это знать?
Re[2]: Приоритет операторов в C++
От: rg45 СССР  
Дата: 24.01.17 07:55
Оценка:
Здравствуйте, RussianFellow, Вы писали:

RF>Ну, например, что означают выражения:


RF>
RF>++*p1 = ++*p2;
RF>


Разыменование двух указателей с последующим инкрементом адресуемых объектов и присваиванием.

Если только все используетые операторы не являются перегруженными, то, согласно пункту 1.9/15 стандарта С++11, такое выражение порождает неопределенное поведение, поскольку скалярный объект, адресуемый указателем p1, модифицируется дважды посредством неупорядоченных побочных эффектов:

If a side effect on a scalar object is unsequenced relative to either another side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined.


В C++03 это поведение регулировалось таким понятием как "точки следования". Начиная с C++11 точки следования были заменены уточненными понятиями, но принцип сохранился.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 24.01.2017 7:57 rg45 . Предыдущая версия .
Re: Приоритет операторов в C++
От: _hum_ Беларусь  
Дата: 24.01.17 09:43
Оценка:
Здравствуйте, RussianFellow, Вы писали:

RF>Я сейчас читаю про приоритет операторов в C++ :


RF>https://ru.wikipedia.org/wiki/%D0%9E%D0%BF%D0%B5%D1%80%D0%B0%D1%82%D0%BE%D1%80%D1%8B_%D0%B2_C_%D0%B8_C%2B%2B#.D0.9F.D1.80.D0.B8.D0.BE.D1.80.D0.B8.D1.82.D0.B5.D1.82.D1.8B_.D0.BE.D0.BF.D0.B5.D1.80.D0.B0.D1.82.D0.BE.D1.80.D0.BE.D0.B2


RF>Мне там не всё понятно. В частности, мне не понятно, что такое левоассоциативность и правоассоциативность операторов в C++.

RF>Не могли бы Вы, уважаемые коллеги, объяснить мне получше, что это такое? А заодно и рассказать понятно про приоритет операторов в C++.
RF>Желательно также привести сложные выражения с операторами и объяснить, что они означают.

RF>Я сейчас читаю Страуструпа, но мне хотелось бы прочитать подробно, понятно, популярно про приоритет операторов в C++.


в википедии лучше читать английские статьи. насчет приоритета и ассоциативности вот эти:
/wiki/Order_of_operations
wiki/Operator_associativity

а если хочется чего-то на пальцах, то набираете в гугле что-нить наподобие "operator precedence associativity" с фильтром по картинкам или видео. и находите, например, это:
05 operator associativity and precedence in c part 1
Re[3]: Приоритет операторов в C++
От: N. I.  
Дата: 26.01.17 11:10
Оценка: +1
SkyKnight:

RF>>P.S. Спрашиваю так потому, что я привык писать сложные выражения в C++ со скобками. А если писать без скобок?

SK>Всегда лучше писать со скобками, иначе потом или читаемость будет хреновая

Зависит от того, кто читает. Мне, например, удобнее читать

if (*first < 0xDC00 && first + 1 != ending && 0xDC00 <= first[1] && first[1] < 0xF000)

вместо

if (((*first) < 0xDC00) && ((first + 1) != ending) && (0xDC00 <= (first[1])) && (first[1] < 0xF000))

и

if (s.empty() || s.front() != lower[i] && s.front() != upper[i])

вместо

if (s.empty() || ((s.front() != (lower[i])) && (s.front() != (upper[i]))))
Re[3]: Приоритет операторов в C++
От: N. I.  
Дата: 26.01.17 11:10
Оценка: 5 (1)
rg45:

R>Если только все используетые операторы не являются перегруженными, то, согласно пункту 1.9/15 стандарта С++11, такое выражение порождает неопределенное поведение, поскольку скалярный объект, адресуемый указателем p1, модифицируется дважды посредством неупорядоченных побочных эффектов:


R>

R>If a side effect on a scalar object is unsequenced relative to either another side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined.


Если p1 и p2 — это обычные указатели, указывающие на разные неперекрывающиеся объекты скалярного типа, отличного от bool, то по правилам C++11 из-за множественной модификации undefined behavior тут возникнуть не может. Модификация результата ++*p1 оператором = выполняется после value computation для ++*p1, а это value computation выполняется после модификации результата *p1 оператором ++. В C++17 ввели ещё более строгую упорядоченность, и там даже такой вариант

++*p = ++*p;

не страшен возникновением undefined behavior за счёт множественной модификации (оно может возникнуть только по другим причинам).
Re[4]: Приоритет операторов в C++
От: rg45 СССР  
Дата: 27.01.17 07:50
Оценка:
Здравствуйте, N. I., Вы писали:

NI>Если p1 и p2 — это обычные указатели, указывающие на разные неперекрывающиеся объекты скалярного типа, отличного от bool, то по правилам C++11 из-за множественной модификации undefined behavior тут возникнуть не может. Модификация результата ++*p1 оператором = выполняется после value computation для ++*p1, а это value computation выполняется после модификации результата *p1 оператором ++.


Да, действительно, это, оказывается, является свойством оператора присваивания:

5.17/1
In all cases, the assignment is sequenced after the value computation of the right and left operands, and before the value computation of the assignment expression.


[Upd]
Да, но как в таком случае понимать пример из 1.9/15?

void f(int, int);
void g(int i, int* v) {
i = v[i++]; // the behavior is undefined
i = 7, i++, i++; // i becomes 9
i = i++ + 1; // the behavior is undefined
i = i + 1; // the value of i is incremented
f(i = -1, i = -1); // the behavior is undefined
}

В обоих выделенных случаях, согласно 5.17/1, присваивание выполняется после модификации (инкремента), происходящего при вычислении правой части. Почему же возникает неопределенное поведение?
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 27.01.2017 8:07 rg45 . Предыдущая версия . Еще …
Отредактировано 27.01.2017 8:00 rg45 . Предыдущая версия .
Re[5]: Приоритет операторов в C++
От: N. I.  
Дата: 27.01.17 09:30
Оценка: 17 (3)
rg45:

R>[Upd]

R>Да, но как в таком случае понимать пример из 1.9/15?

R>

R>

R>void f(int, int);
R>void g(int i, int* v) {
R>i = v[i++]; // the behavior is undefined
R>i = 7, i++, i++; // i becomes 9
R>i = i++ + 1; // the behavior is undefined
R>i = i + 1; // the value of i is incremented
R>f(i = -1, i = -1); // the behavior is undefined
R>}
R>

R>В обоих выделенных случаях, согласно 5.17/1, присваивание выполняется после модификации (инкремента)

До C++17 эти две модификации не были упорядочены между собой, т.к. здесь инкремент — постфиксный, а у него модификация следует за вычислением значения (у префиксного же вычисление значения следует за модификацией, что в рассмотренном ранее примере позволяет выстроить упорядоченную цепочку модификация -> вычисление значения -> модификация).
Отредактировано 27.01.2017 12:08 N. I. . Предыдущая версия .
Re[6]: Приоритет операторов в C++
От: uzhas Ниоткуда  
Дата: 27.01.17 10:27
Оценка: 1 (1)
Здравствуйте, N. I., Вы писали:

NI>Эти две модификации не упорядочены между собой, т.к. здесь инкремент — постфиксный, а у него модификация следует за вычислением значения (у префиксного же вычисление значения следует за модификацией, что в рассмотренном ранее примере позволяет выстроить упорядоченную цепочку модификация -> вычисление значения -> модификация).


правильно ли я понимаю, что в этом коде нет UB?
void g(int i, int* v) {
  i = v[++i];
  i = ++i + 1;
}
Re[7]: Приоритет операторов в C++
От: N. I.  
Дата: 27.01.17 11:26
Оценка:
uzhas:

U>правильно ли я понимаю, что в этом коде нет UB?

U>
U>void g(int i, int* v) {
U>  i = v[++i];
U>  i = ++i + 1;
U>}
U>


Если эту функцию вызывают с подходящими аргументами (так, что в результате не получается разыменование нулевого указателя, выход за границы массива, вычисление значения неинициализированного объекта, целочисленное переполнение и т.д.), то в случае с C++11/14/17 undefined behavior тут не будет.

В соответствии с

If x is not of type bool, the expression ++x is equivalent to x+=1


поведение в этом примере должно быть такое же, как у

void g(int i, int* v) {
  i = v[i += 1];
  i = (i += 1) + 1;
}

Дальше на основании

In all cases, the assignment is sequenced after the value computation of the right and left operands, and before the value computation of the assignment expression.

и

The value computations of the operands of an operator are sequenced before the value computation of the result of the operator.


можно выстроить цепочки

+= assignment → += value computation → [] value computation → = assignment
+= assignment → += value computation → + value computation → = assignment
Re[6]: Приоритет операторов в C++
От: rg45 СССР  
Дата: 27.01.17 13:33
Оценка:
Здравствуйте, N. I., Вы писали:

R>>

R>>

R>>void f(int, int);
R>>void g(int i, int* v) {
R>>i = v[i++]; // the behavior is undefined
R>>i = 7, i++, i++; // i becomes 9
R>>i = i++ + 1; // the behavior is undefined
R>>i = i + 1; // the value of i is incremented
R>>f(i = -1, i = -1); // the behavior is undefined
R>>}
R>>


NI>До C++17 эти две модификации не были упорядочены между собой, т.к. здесь инкремент — постфиксный, а у него модификация следует за вычислением значения...


Что-то я как-то медленно соображать стал. Выходит, что в случае использования постинкремента, в отличие от преинкремента, применение побочного эфффекта не является необходимым для вычисления правой части оператора присваивания, таким образом инкремент и присваивание оказываются неупорядоченными друг с другом. Правильно?

В этом контексте нелишним будет упомянуть правила для постфиксных инкремента/декремента:

The value computation of the ++ expression is sequenced before the modification of the operand object.


Таким образом, при использовании постинкремента в правой части, мы знаем только то, что и присваивание и инкремент произойдут после вычисления значения подвыражения преинкремента, но в каком порядке — неизвестно.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 27.01.2017 14:28 rg45 . Предыдущая версия . Еще …
Отредактировано 27.01.2017 14:26 rg45 . Предыдущая версия .
Отредактировано 27.01.2017 14:11 rg45 . Предыдущая версия .
Re[7]: Приоритет операторов в C++
От: N. I.  
Дата: 27.01.17 14:39
Оценка:
rg45:

R>Таким образом, при использовании постинкремента в правой части, мы знаем только, и присваивание и инкремент произойдут после value computation, но в каком порядке — неизвестно.


Примерно так, но тут следует уточнить, что порядок не неизвестен, его вообще не существует. Наличие неизвестного порядка и отсутствие какого-либо порядка — это две разные ситуации. Две unsequenced модификации одного скалярного объекта приводят к undefined behavior, а две indeterminately sequenced модификации одного скалярного объекта — нет. В данном случае у нас именно unsequenced modifications.

В C++17 строгий порядок в таком примере будет обеспечен за счёт нового правила для операторов присваивания:

The right operand is sequenced before the left operand.

Отредактировано 27.01.2017 14:51 N. I. . Предыдущая версия .
Re[3]: Приоритет операторов в C++
От: alzt  
Дата: 08.02.17 05:35
Оценка:
Здравствуйте, Andrew.W Worobow, Вы писали:

RF>>P.S. Спрашиваю так потому, что я привык писать сложные выражения в C++ со скобками. А если писать без скобок?


AWW>лучше все равно всегда писать со скобками за редким-редким исключением.


Да и вообще писать так, чтобы скобки не требовались.
Вчера несколько часов отлаживал ошибку после неудачной копипасты выражения с "i++".
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.