Это неопределенное поведение, если несколько операндов в составном выражении изменяют один и тот же объект?

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

Я полагаю, что пример этого UB показан в приведенном ниже коде, однако я скомпилировал на g ++, clang ++ и visual studio, и все они распечатывают одни и те же значения и не могут привести к непредсказуемым значениям в разных компиляторах.

#include <iostream>

int a( int& lhs ) { lhs -= 4; return lhs; }
int b( int& lhs ) { lhs *= 7; return lhs; }
int c( int& lhs ) { lhs += 1; return lhs; }
int d( int& lhs ) { lhs += 2; return lhs; }
int e( int& lhs ) { lhs *= 3; return lhs; }

int main( int argc, char **argv )
{
int i = 100;
int j = ( b( i ) + c( i ) ) * e( i ) / a( i ) * d( i );

std::cout << i << ", " << j << std::endl;

return 0;
}

Это поведение не определено, или я как-то вызвал описание предполагаемого UB, которое на самом деле не определено?

Я был бы признателен, если бы кто-то мог опубликовать пример этого UB и, возможно, даже указать мне, где в стандарте C ++ говорится, что это UB.

27

Решение

Нет. О неопределенном поведении здесь не может быть и речи (при условии, что int арифметика не переполняется): все модификации i изолированы точками последовательности (используя терминологию C ++ 03). Есть точка последовательности на входе в каждую функцию, и есть точка последовательности на выходе.

Поведение неопределенные Вот.

Ваш код фактически следует той же схеме, что и классический пример, который часто используется для иллюстрации разницы между не определено а также неопределенные поведение. Учти это

int i = 1;
int j = ++i * ++i;

Люди часто утверждают, что в этом примере «результат не зависит от порядка оценки и, следовательно, j всегда должно быть 6 «. Это неверное утверждение, поскольку поведение не определено.

Однако в этом примере

int inc(int &i) { return ++i; }

int i = 1;
int j = inc(i) * inc(i);

поведение формально только не определено. А именно, порядок оценки не уточняется. Однако, поскольку результат выражения не зависит от порядка оценки вообще, j гарантированно всегда заканчивается 6, Это пример того, как в целом опасная неопределенная комбинация поведения может привести к идеально определенному результату.

В вашем случае результат вашего выражения критически зависит от порядка оценки, что означает, что результат будет непредсказуемым. Тем не менее, здесь нет неопределенного поведения, то есть программе не разрешено форматировать ваш жесткий диск. Разрешено производить непредсказуемый результат только в j,

Постскриптум Опять же, может оказаться, что некоторые сценарии оценки для вашего выражения приводят к переполнению целых чисел со знаком (я не проанализировал их все), что само по себе вызывает неопределенное поведение. Таким образом, все еще существует потенциал для неопределенного поведения, приводящего к неопределенному поведению в вашем выражении. Но это, вероятно, не то, о чем ваш вопрос.

34

Другие решения

Нет, это не неопределенное поведение.

Но это вызывает неуточненное поведение.

Это связано с тем, что порядок вычисления подвыражений не указан.

int j = ( b( i ) + c( i ) ) * e( i ) / a( i ) * d( i );

В вышеприведенном выражении подвыражения:

b(i)
c(i)
e(i)
a(i)
d(i)

Может оцениваться в любом порядке. Поскольку все они имеют побочные эффекты, результаты будут зависеть от этого порядка.

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

tmp_1 = b(i)           // A
tmp_2 = c(i)           // B
tmp_3 = e(i)           // C
tmp_4 = a(i)           // D
tmp_5 = d(i)           // E

tmp_6 = tmp_1 + tmp_2  // F   (Happens after A and B)
tmp_7 = tmp_6 * tmp_3  // G   (Happens after C and F)
tmp_8 = tmp_7 / tmp_4  // H   (Happens after D and G)
tmp_9 = tmp_8 * tmp_5  // I   (Happens after E and H)

int j = tmp_9;         // J   (Happens after I)
12

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

Однако порядок вычисления выражения не указан. В результате вы можете получить разные результаты, если порядок оценки изменится. Это не неопределенное поведение: результат — один из всех возможных порядков оценки. Неопределенное поведение означает, что программа может вести себя так, как она хочет, включая получение «ожидаемых» (ожидаемых программистом) результатов для рассматриваемого выражения при одновременном обрезании всех других данных.

8
По вопросам рекламы [email protected]