Точки последовательности и побочные эффекты в C

В этом C-FAQ рассказывается о точка последовательности;

В Стандарте указано, что
Между предыдущей и следующей точкой последовательности сохраненное значение объекта должно быть изменено не более одного раза при вычислении выражения. Кроме того, доступ к предыдущему значению должен осуществляться только для определения сохраняемого значения.

В примерах

i = i++;
a[i] = i++;

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

во втором предложении говорится: если объект записывается в полном выражении, любой доступ к нему в пределах одного и того же выражения должен непосредственно участвовать в вычислении записываемого значения. Это правило эффективно ограничивает допустимые выражения теми, в которых доступ явно предшествует модификации. Например, старый резерв

 i = i + 1 

разрешено, так как доступ к i используется для определения окончательного значения i. Пример

a[i] = i++

запрещено, потому что один из доступов к i (тот, что в a[i]) не имеет ничего общего со значением, которое в конечном итоге сохраняется в i (что происходит в i++), и поэтому нет хорошего способа определить.

Мои вопросы:
1.Что это значит, если объект записывается в полное выражение, то любой и все доступы к нему в одном и том же выражении должны непосредственно участвовать в вычислении значения для быть написано.?

2.что это означает. Пример a[i] = i++ запрещен, потому что один из доступов к i (тот, что в a[i]) не имеет ничего общего со значением, которое в конечном итоге сохраняется в i (что происходит в течение в i++)
Может ли кто-нибудь объяснить это простым способом?


person haccks    schedule 04.07.2013    source источник


Ответы (3)


Мой вопрос: 1. Что это означает, если объект записывается в рамках полного выражения, любой и все доступы к нему в рамках одного и того же выражения должны непосредственно участвовать в вычислении записываемого значения?

С подвыражением, таким как i++, записывается i. Более того, присваивание — это выражение, поэтому в i = 2 записывается i. Может быть не сразу очевидно, что a = b является выражением, но это так. Вот почему вы можете делать такие вещи, как a = b = c, что хорошо, и if (a = b), что менее хорошо.

Итак, это говорит о том, что если вы записываете в i с =, либо до, либо после увеличения, то любой доступ к i должен быть частью вычисления нового значения i. Однако, и это важно, единственное, что участвует в расчете приращения до и после, — это значение i в начале инструкции.

2. что это означает. Пример a[i] = i++ запрещен, поскольку один из доступов к i (тот, что в a[i]) не имеет ничего общего со значением, которое в конечном итоге сохраняется в i (которое происходит в i++)

Именно то, что он говорит. Когда вы обращаетесь к i в a[i], это не является частью вычисления нового значения i, полученного из i++.

Может ли кто-нибудь объяснить это простым способом?

Простой способ: не используйте приращение до или после в выражении. всегда используйте их в утверждениях сами по себе. Если вам это действительно необходимо, НЕ используйте одну и ту же переменную где-либо еще во всем операторе.

person Tom Tanner    schedule 04.07.2013

Это объяснение странное, и я не могу его понять.

Верное объяснение состоит в том, что выражение i++ имеет побочный эффект (увеличение i), который можно применить в любое время после того, как это конкретное i будет вычислено.

Поскольку за пределами точек последовательности язык C не гарантирует порядок вычисления или время применения постинкрементов (вероятно, из соображений производительности), во втором примере могут произойти три вещи (предположим, i = 5 перед строкой):

  • Крайний левый i (тот, что в a[i]) оценивается первым, чтобы вычислить адрес a[i] для хранения. Затем оценивается самый правый i, затем применяется пост-инкремент: Строка выполняет a[5] = 5; i=6;
  • Сначала оценивается самый правый i, затем самый левый, а затем i применяется пост-инкремент, когда все сказано и сделано: в этом случае эффекты идентичны предыдущему случаю.
  • Сначала оценивается самый правый i, сразу же применяется постинкремент, а затем оценивается самый левый i для вычисления адреса a[i] для хранения. На этот раз эффект a[6] = 5; i=6.

Выбор результата зависит не только от выбора компилятора, но и от настроек компилятора: Microsoft Visual C++ может давать разные результаты в зависимости от того, компилируете ли вы в режиме отладки или в режиме выпуска.

person Medinoc    schedule 04.07.2013
comment
Ваше объяснение касается UB a[i] = i++, и вы объяснили это хорошо, но мне нужно объяснение моих вопросов. - person haccks; 04.07.2013
comment
Увы, я не вижу пояснений к цитируемым вами частям. Мне они кажутся бессмысленными. - person Medinoc; 04.07.2013
comment
Да, для меня это тоже бессмысленно, поэтому я и задал этот вопрос. - person haccks; 04.07.2013
comment
Ваше объяснение звучит так, будто существует небольшое количество возможных порядков, и один из них произойдет. Это соответствовало бы концепции неопределенного поведения в стандарте. В стандарте говорится, что a[i] = i++; является неопределенным поведением (6.5:2 в C99), поэтому ваше объяснение, каким бы «верным» оно вам ни казалось, не эквивалентно стандарту. - person Pascal Cuoq; 04.07.2013
comment
В контексте этого одного примера существует столько перестановок A, B и C, когда C не может произойти до B... В контексте такого вычисления это скорее либо-или неопределенное, а не гнусавые демоны undefined ... - person Medinoc; 04.07.2013
comment
@Medinoc Есть название для «либо-либо не определено». Это имя «не указано». В контексте этого одного примера стандарт намеренно избегает использования этого имени, вместо этого используя «неопределенное поведение». Это позволяет компилятору заставить демонов вылететь из вашего носа, если вы скомпилируете или запустите эту программу. - person Pascal Cuoq; 04.07.2013
comment
@Medinoc: Чтобы следовать комментарию Паскаля, компилятору разрешено предполагать, что вы не пишете код, который вызывает UB, и, таким образом, соответствующим образом проводить оптимизацию (и т. Д.). Если вы делаете такой код, то предположение неверно, и результирующее поведение может быть совершенно неожиданным. - person Oliver Charlesworth; 04.07.2013

Наконец я получил объяснение на SO по этому поводу. Прочитав его и часто задаваемые вопросы я пришел к следующему выводу;

1.Последнее предложение

Кроме того, доступ к предыдущему значению должен осуществляться только для определения сохраняемого значения

было бы так;

Кроме того, доступ к предыдущему значению объекта должен осуществляться только для определения измененного/нового значения (того же объекта), которое должно быть сохранено.

Как видно на примере

 int i = 1, j, a[5];    
 i = i + 1;
 j = i + 1;
 a[i] = i; 

в случае выражения i = i + 1 предшествующее значение (которое здесь 1) из i (в RHS) используется для определения значения i, которое должно быть сохранено, и это то, что оператор

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

говорит.
В то время как в случае j = i + 1 и a[i] = i доступное значение i является просто значением не предыдущим значением, поскольку нет, где i изменены в этих утверждениях.

2. Второй вопрос можно объяснить так:
В случае выражения a[i] = i++ или a[i++] = i первое предложение приведенного выше утверждения

Между предыдущей и следующей точкой последовательности сохраненное значение объекта должно быть изменено не более одного раза при вычислении выражения.

получить ошибку, так как i изменяется только один раз между двумя последовательными точками следования. И именно поэтому нам нужно второе предложение.
Оба эти примера запрещены в C, потому что предыдущее значение i используется дважды, т. е. i++ само обращается к предыдущему значению i в выражении, чтобы изменить его. и, следовательно, другой доступ к предыдущему значению/значению i не нужен, поскольку он не используется для определения измененного значения, которое необходимо сохранить.

person haccks    schedule 13.07.2013
comment
эй, @hackcks, согласно вашему второму объяснению, вы предположили, что значение, которое мы записываем, равно «i», но на самом деле это [i]... путаница для меня. пожалуйста, покажите мне путь! - person OldSchool; 24.03.2014
comment
@Bayant_singh; В какой строке? - person haccks; 24.03.2014
comment
Между предыдущей и следующей точкой последовательности сохраненное значение объекта должно быть изменено не более одного раза путем вычисления выражения. - person OldSchool; 24.03.2014
comment
пожалуйста, скажите мне, где я могу найти полную ссылку на это - person OldSchool; 24.03.2014
comment
Выражение i++ записывается в i. В a[i] = i++ есть две операции записи: одна на ++i) и одна на =a[i]). - person haccks; 24.03.2014
comment
да. Но Вы можете видеть i++ как i = i + 1. В этом случае вы также пишете i. - person haccks; 24.03.2014
comment
две операции записи над разными операндами могут привести к UB? Я думаю, что это UB, если выполняются операции (более одного раза) над одним и тем же операндом, а не над двумя операндами в выражении? Друг, у тебя есть еще одна головоломка для меня..;) - person OldSchool; 24.03.2014
comment
меня смущает утверждение Между предыдущей и следующей точкой последовательности объект должен иметь свое сохраненное значение, измененное не более одного раза путем оценки выражения. речь идет об объекте, а не о двух в одном выражении - person OldSchool; 24.03.2014
comment
@Bayant_singh; две операции записи в разные операнды могут привести к UB?: Нет. Ex- a[i] = j++ две операции записи, но он не будет вызывать UB. - person haccks; 24.03.2014
comment
@Bayant_singh; Разве вы не говорили во втором объяснении, что в этом случае Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. провалилась. Так что забудьте это утверждение для второго объяснения. - person haccks; 24.03.2014
comment
но вы написали, что он изменяется только один раз между двумя последовательными точками последовательности. и я не могу выбрать две последовательные точки последовательности, о которых вы говорите? - person OldSchool; 24.03.2014
comment
@Bayant_singh; Прочтите это, чтобы узнать о контрольной точке: en.wikipedia.org/wiki/Sequence_point - person haccks; 24.03.2014