C ++ 98 и C ++ 03
Этот ответ для более старых версий стандарта C ++. Версии стандарта на C ++ 11 и C ++ 14 формально не содержат «точек последовательности»; вместо этого операции «секвенируются до», «не секвенированы» или «неопределенно секвенированы». Чистый эффект по сути тот же, но терминология другая.
Отказ от ответственности : хорошо. Этот ответ немного длинный. Так что наберитесь терпения, читая его. Если вы уже знаете эти вещи, их повторное чтение не сойдет с ума.
Пререквизиты : элементарные знания C ++ Standard
Каковы Очки Последовательности?
Стандарт говорит
В определенных точках в последовательности выполнения, называемых точками последовательности , все побочные эффекты предыдущих оценок должны быть завершены, и никаких побочных эффектов последующих оценок не должно быть. (§1.9 / 7)
Побочные эффекты? Каковы побочные эффекты?
Оценка выражения производит что-то, и если, кроме того, происходит изменение в состоянии среды выполнения, говорят, что выражение (его оценка) имеет некоторый побочный эффект (ы).
Например:
int x = y++; //where y is also an int
В дополнение к операции инициализации значение y
изменяется из-за побочного эффекта ++
оператора.
Все идет нормально. Переходя к точкам последовательности. Определение чередования seq-точек, данное автором comp.lang.c Steve Summit
:
Точка последовательности - это момент времени, когда пыль осела, и все побочные эффекты, которые были замечены до сих пор, гарантированно будут завершены.
Какие общие точки последовательности перечислены в Стандарте C ++?
Это:
в конце оценки полного выражения ( §1.9/16
) (полное выражение - это выражение, которое не является подвыражением другого выражения.) 1
Пример :
int a = 5; // ; is a sequence point here
в оценке каждого из следующих выражений после оценки первого выражения ( §1.9/18
) 2
a && b (§5.14)
a || b (§5.15)
a ? b : c (§5.16)
a , b (§5.18)
(здесь a, b - оператор запятой; in func(a,a++)
,
- не оператор запятой, это просто разделитель между аргументами a
и a++
. Таким образом, поведение в этом случае не определено (если a
считается типом примитива))
при вызове функции (независимо от того, является ли функция встроенной), после оценки всех аргументов функции (если таковые имеются), которая происходит перед выполнением любых выражений или операторов в теле функции ( §1.9/17
).
1: Примечание: оценка полного выражения может включать оценку подвыражений, которые не являются лексической частью полного выражения. Например, подвыражения, участвующие в оценке выражений аргумента по умолчанию (8.3.6), считаются созданными в выражении, которое вызывает функцию, а не в выражении, которое определяет аргумент по умолчанию.
2: Указанные операторы являются встроенными операторами, как описано в разделе 5. Когда один из этих операторов перегружен (раздел 13) в допустимом контексте, что обозначает пользовательскую функцию оператора, выражение обозначает вызов функции и операнды формируют список аргументов, без подразумеваемой точки последовательности между ними.
Что такое неопределенное поведение?
Стандарт определяет неопределенное поведение в разделе §1.3.12
как
поведение, которое может возникнуть при использовании ошибочной программной конструкции или ошибочных данных, к которым настоящий международный стандарт не предъявляет никаких требований 3 .
Неопределенное поведение также может ожидаться, когда в этом международном стандарте опущено описание любого явного определения поведения.
3: допустимое неопределенное поведение варьируется от полного игнорирования ситуации с непредсказуемыми результатами до поведения во время перевода или выполнения программы документированным образом, характерным для среды (с выдачей диагностического сообщения или без него), до прекращения перевода или выполнения (с выдачей диагностического сообщения).
Короче говоря, неопределенное поведение означает, что может произойти все, что угодно: от демонов, вылетающих из носа, до беременности вашей подруги.
Какова связь между неопределенным поведением и точками последовательности?
Прежде чем я углублюсь в это, вы должны знать разницу между неопределенным поведением, неопределенным поведением и реализацией, определяемой реализацией .
Вы также должны это знать the order of evaluation of operands of individual operators and subexpressions of individual expressions, and the order in which side effects take place, is unspecified
.
Например:
int x = 5, y = 6;
int z = x++ + y++; //it is unspecified whether x++ or y++ will be evaluated first.
Еще один пример здесь .
Теперь стандарт в §5/4
говорит
- 1) Между предыдущей и следующей точкой последовательности скалярный объект должен иметь свое сохраненное значение, измененное не более одного раза путем оценки выражения.
Что это значит?
Неформально это означает, что между двумя точками последовательности переменная не должна изменяться более одного раза. В выражении-выражении next sequence point
обычно previous sequence point
находится в конце точки с запятой, а в конце предыдущего. Выражение также может содержать промежуточный sequence points
.
Из вышеприведенного предложения следующие выражения вызывают неопределенное поведение:
i++ * ++i; // UB, i is modified more than once btw two SPs
i = ++i; // UB, same as above
++i = 2; // UB, same as above
i = ++i + 1; // UB, same as above
++++++i; // UB, parsed as (++(++(++i)))
i = (i, ++i, ++i); // UB, there's no SP between `++i` (right most) and assignment to `i` (`i` is modified more than once btw two SPs)
Но следующие выражения хороши:
i = (i, ++i, 1) + 1; // well defined (AFAIK)
i = (++i, i++, i); // well defined
int j = i;
j = (++i, i++, j*i); // well defined
- 2) Кроме того, предварительное значение должно быть доступно только для определения значения, которое будет сохранено.
Что это значит? Это означает, что если объект записан в пределах полного выражения, любой и все обращения к нему в пределах одного и того же выражения должны быть непосредственно вовлечены в вычисление значения, которое будет записано .
Например, во i = i + 1
всех случаях доступа i
(в LHS и в RHS) непосредственно участвуют в вычислении значения для записи. Так что все в порядке.
Это правило эффективно ограничивает юридические выражения теми, в которых доступы явно предшествуют модификации.
Пример 1:
std::printf("%d %d", i,++i); // invokes Undefined Behaviour because of Rule no 2
Пример 2:
a[i] = i++ // or a[++i] = i or a[i++] = ++i etc
не допускается, потому что один из доступов i
(тот, что в a[i]
) не имеет ничего общего со значением, которое в итоге сохраняется в i (что происходит в i++
), и поэтому нет хорошего способа определить - ни для нашего понимания, ни для компилятор - должен ли доступ осуществляться до или после сохранения увеличенного значения. Таким образом, поведение не определено.
Пример 3:
int x = i + i++ ;// Similar to above
Последующий ответ для C ++ 11 здесь .
*p++ = 4
не является неопределенным поведением.*p++
интерпретируется как*(p++)
.p++
возвращаетсяp
(копия) и значение сохраняется по предыдущему адресу. Почему это вызывает UB? Это прекрасно.++i
и назначением отсутствует точка последовательностиi
. Второе выражение не вызывает UB, потому что выражениеi
не меняет значениеi
. Во втором примере передi++
последовательным,
оператором присваивания следует точка точки ( ).Это продолжение моего предыдущего ответа и содержит материал, связанный с C ++ 11. ,
Пререквизиты : элементарные знания по связям (математика).
Правда ли, что в C ++ 11 нет точек последовательности?
Да! Это очень верно.
Последовательность Очки были заменены Sequenced Перед и Sequenced После (и Unsequenced и неопределенно Sequenced ) отношений в C ++ 11.
Что именно это за последовательность?
Sequenced Before (§1.9 / 13) - это отношение, которое:
между оценками, выполняемыми одним потоком и вызывает строгий частичный порядок 1
Формально это означает , учитывая любые две оценки (см ниже)
A
иB
, еслиA
это последовательность доB
того , то выполнениеA
должно предшествовать выполнениеB
. ЕслиA
не секвенировали до тогоB
иB
не секвенировали до тогоA
, тоA
иB
являются unsequenced 2 .Оценки
A
иB
являются неопределенно секвенировали , когда либоA
секвенировали перед темB
илиB
секвенируют перед темA
, но это не определено , которое 3 .[ПРИМЕЧАНИЯ]
1: Строгий частичный порядок является бинарное отношение
"<"
на множестве ,P
которое являетсяasymmetric
, иtransitive
, т.е. для всехa
,b
иc
вP
, имеем:........ (я). если a <b, то ¬ (b <a) (
asymmetry
);........ (II). если a <b и b <c, то a <c (
transitivity
).2: выполнение непоследовательных оценок может перекрываться .
3: Оценки с неопределенной последовательностью не могут перекрываться , но любая из них может быть выполнена первой.
Что означает слово «оценка» в контексте C ++ 11?
В C ++ 11 оценка выражения (или подвыражения) в целом включает в себя:
вычисление значений (включая определение идентификатора объекта для оценки glvalue и выборку значения, ранее назначенного объекту для оценки prvalue ) и
инициирование побочных эффектов .
Теперь (§1.9 / 14) говорит:
Тривиальный пример:
int x;
x = 10;
++x;
Вычисление значения и связанный с
++x
ним побочный эффект секвенируется после вычисления значения и побочного эффектаx = 10;
Таким образом, должна быть некоторая связь между неопределенным поведением и вышеупомянутыми вещами, верно?
Да! Правильно.
В (§1.9 / 15) было упомянуто, что
Например :
+
оператора не последовательна относительно друг друга.<<
и>>
операторов не секвенированы относительно друг друга.4: В выражении, которое оценивается более одного раза во время выполнения программы, непоследовательные и неопределенно последовательные оценки ее подвыражений не должны выполняться последовательно в различных оценках.
Это означает, что при
x + y
вычислении значенияx
иy
последовательности перед вычислением значения(x + y)
.Важнее
Примеры:
i = i++ * ++i; // Undefined Behaviour
i = ++i + i++; // Undefined Behaviour
i = ++i + ++i; // Undefined Behaviour
i = v[i++]; // Undefined Behaviour
i = v[++i]: // Well-defined Behavior
i = i++ + 1; // Undefined Behaviour
i = ++i + 1; // Well-defined Behaviour
++++i; // Well-defined Behaviour
f(i = -1, i = -1); // Undefined Behaviour (see below)
Выражения
(5)
,(7)
и(8)
не вызывают неопределенное поведение. Проверьте следующие ответы для более подробного объяснения.Заключительное примечание :
Если вы найдете какой-либо недостаток в сообщении, пожалуйста, оставьте комментарий. Опытные пользователи (с rep> 20000), пожалуйста, не стесняйтесь редактировать пост для исправления опечаток и других ошибок.
источник
f(i = -1, i = 1)
?++i
(будучи оцененным значением перед+
оператором, который его использует), стандарт все еще не говорит, что его побочный эффект должен быть закончен. Но на самом деле, поскольку он возвращает ссылку на a,lvalue
которыйi
сам по себе, он ДОЛЖЕН закончить побочный эффект, поскольку оценка должна быть завершена, поэтому значение должно быть актуальным. Это была сумасшедшая часть, чтобы получить на самом деле.C ++ 17 (
N4659
) включает предложение « Уточнение порядка оценки выражений для Idiomatic C ++», в котором определяется более строгий порядок вычисления выражений.В частности, следующее предложение
вместе со следующим уточнением
сделать допустимым несколько случаев ранее неопределенного поведения, включая рассматриваемый:
Однако несколько других подобных случаев все еще приводят к неопределенному поведению.
В
N4140
:Но в
N4659
Конечно, использование совместимого с C ++ 17 компилятора не обязательно означает, что нужно начинать писать такие выражения.
источник
i = i++ + 1;
определяется поведение в c ++ 17, я думаю, даже если «правый операнд секвенирован перед левым операндом», однако модификация «i ++» и побочный эффект для присваивания не секвенированы, пожалуйста, дайте больше подробностей для их интерпретацииЯ предполагаю, что есть фундаментальная причина для изменения, а не просто косметически, чтобы сделать старую интерпретацию более ясной: эта причина - параллелизм. Неуказанный порядок разработки - это просто выбор одного из нескольких возможных серийных порядков, он сильно отличается от порядков до и после, потому что если нет определенного порядка, возможна параллельная оценка: не так со старыми правилами. Например в:
ранее либо a затем b, либо, b затем a. Теперь, a и b могут быть оценены с чередованием инструкций или даже на разных ядрах.
источник
По-
C99(ISO/IEC 9899:TC3)
видимому, в этом обсуждении пока отсутствуют следующие элементы, касающиеся порядка оценки.источник