Nieokreślone zachowanie i punkty sekwencji

Co to są "punkty sekwencji"?

Jaka jest relacja między nieokreślonym zachowaniem a punktami sekwencji?

Często używam zabawnych i zawiłych wyrażeń, takich jak a[++i] = i;, aby poczuć się lepiej. Dlaczego mam przestać ich używać?

Jeśli to przeczytałeś, koniecznie odwiedź kolejne pytanie Undefined behavior and sequence points.

_(Uwaga: to ma być wpis do [Stack Overflow ' S C++ FAQ] (https://stackoverflow.com/questions/tagged/c++ - faq). Jeśli chcesz krytykować pomysł dostarczenia FAQ w tej formie, to [post na meta, który to wszystko zaczął] (https://meta.stackexchange.com/questions/68647/setting-up-a-faq-for-the-c-tag miejsce). Odpowiedzi na to pytanie są monitorowane w [C++ chatroom](https://chat.stackoverflow.com/rooms/10/c-lounge), gdzie w pierwszej kolejności zaczął się pomysł FAQ, więc Twoja odpowiedź najprawdopodobniej zostanie przeczytana przez tych kto wpadł na ten pomysł.)_
Author: Prasoon Saurav, 2010-11-14

5 answers

C++98 i C++03

Ta odpowiedź dotyczy starszych wersji standardu C++. Wersje C++11 i C++14 standardu nie zawierają formalnie "punktów sekwencji"; operacje są "sekwencjonowane przed" lub "unsequenced" lub "indeterminately sequenced". Efekt netto jest zasadniczo taki sam, ale terminologia jest inna.


Disclaimer : Ok. Ta odpowiedź jest trochę długa. Więc miej cierpliwość podczas czytania. Jeśli już wiesz te rzeczy, przeczytanie ich jeszcze raz nie sprawi, że oszalejesz.

Pre-requisites : elementarna wiedza C++ Standard


Co to są Punkty sekwencji?

Norma mówi

W określonych punktach sekwencji wykonawczej zwanych punktami sekwencji , wszystkie efekty uboczne poprzednich ocen jest kompletna i nie ma skutków ubocznych kolejnych ocen. (§1.9/7)

Skutki uboczne? Jakie są skutki uboczne?

Ewaluacja wyrażenia daje coś i jeśli dodatkowo nastąpi zmiana stanu środowiska wykonawczego, mówi się, że wyrażenie (jego ewaluacja) ma jakieś skutki uboczne.

Na przykład:

int x = y++; //where y is also an int

Oprócz operacji inicjalizacji wartość y jest zmieniana z powodu efektu ubocznego operatora ++.

Jak na razie dobrze. Przechodząc do sekwencji punktów. Alternatywna definicja punktów seq podana przez komp.lang.autor c Steve Summit:

Punkt sekwencji jest punktem w czasie, w którym pył osiadłszy i wszystkie efekty uboczne, które zostały zaobserwowane do tej pory, są gwarantowane.


Jakie są wspólne punkty sekwencji wymienione w standardzie C++?

Są to:

  • Na końcu oceny wyrażenia pełnego (§1.9/16) (wyrażenie pełne jest wyrażeniem, które jest nie jest to podwyrażenie innego wyrażenia.)1

    Przykład:

    int a = 5; // ; is a sequence point here
    
  • W ocenie każdego z poniższych wyrażeń po ocenie pierwszego wyrażenia (§1.9/18) 2

    • a && b (§5.14)
    • a || b (§5.15)
    • a ? b : c (§5.16)
    • a , b (§5.18) (tutaj a, b jest operatorem przecinka; w func(a,a++) , nie jest operatorem przecinków, jest jedynie separatorem pomiędzy argumentami a i a++. Tak więc zachowanie jest nieokreślone w w tym przypadku (jeśli a jest uważany za Typ prymitywny))
  • Przy wywołaniu funkcji (niezależnie od tego, czy funkcja jest inline), po ewaluacji wszystkich argumentów funkcji (jeśli istnieją), które ma miejsce przed wykonaniem jakichkolwiek wyrażeń lub poleceń w ciele funkcji (§1.9/17).

1 : Uwaga : ocena wyrażenia pełnego może obejmować ocenę podwyrażeń, które nie są leksykalne część pełnego wyrazu. Na przykład, podwyrażenia związane z oceną domyślnych wyrażeń argumentów (8.3.6) są uważane za utworzone w wyrażeniu, które wywołuje funkcję, a nie w wyrażeniu, które definiuje domyślny argument

2 : wskazane operatory są operatorami wbudowanymi, jak opisano w klauzuli 5. Gdy jeden z tych operatorów jest przeciążony (klauzula 13) w prawidłowym kontekście, oznaczając w ten sposób funkcję operatora zdefiniowaną przez użytkownika, wyrażenie oznacza wywołanie funkcji a operandy tworzą listę argumentów, bez implikowanego punktu sekwencji między nimi.


Co To jest nieokreślone zachowanie?

Norma definiuje nieokreślone zachowanie w sekcji §1.3.12 jako

Zachowanie, które może powstać przy użyciu błędnej konstrukcji programu lub błędnych danych, dla których ten Międzynarodowy Standard nie nakłada żadnych wymagań 3.

Undefined behavior można również oczekiwać, gdy to Międzynarodowy Standard pomija opis jakiejkolwiek wyraźnej definicji zachowania.

3 : dopuszczalne nieokreślone zachowanie waha się od ignorowania sytuacji całkowicie z nieprzewidywalnymi rezultatami, do zachowania podczas tłumaczenia lub wykonywania programu w udokumentowany sposób charakterystyczny dla środowiska (z lub z- z wydaniem komunikatu diagnostycznego), do zakończenia tłumaczenia lub wykonania (z wydaniem komunikatu diagnostycznego).

W krótkie, nieokreślone zachowanie oznacza, że wszystko może się zdarzyć od demonów wylatujących z twojego nosa do twojej dziewczyny, która zajdzie w ciążę.


Jaka jest relacja między nieokreślonym zachowaniem a punktami sekwencji?

Zanim przejdę do tego musisz znać różnicę między niezdefiniowanym zachowaniem, nieokreślonym zachowaniem i zdefiniowanym zachowaniem implementacji .

Musisz też o tym wiedzieć 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.

Na przykład:

int x = 5, y = 6;

int z = x++ + y++; //it is unspecified whether x++ or y++ will be evaluated first.

Inny przykład tutaj .


Teraz Standard w §5/4 mówi

  • 1) pomiędzy poprzednim i następnym punktem sekwencji obiekt skalarny ma zapisaną wartość modyfikowaną co najwyżej raz przez ewaluację wyrażenia.
Co to znaczy?

Nieformalnie oznacza to, że pomiędzy dwoma punktami sekwencji zmienna nie może być modyfikowana więcej niż raz. W wyrażeniu next sequence point znajduje się zwykle na końcu średnika, a previous sequence point znajduje się na końcu poprzedniego stwierdzenia. Wyrażenie może również zawierać pośredni sequence points.

Z powyższego zdania następujące wyrażenia wywołują nieokreślone zachowanie:

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)

Ale następujące wyrażenia są w porządku:

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) ponadto dostęp do poprzedniej wartości jest możliwy tylko w celu określenia wartości, która ma być przechowywana.
Co to znaczy? Oznacza to, że jeśli obiekt jest zapisany w pełnym wyrażeniu, każdy dostęp do niego w ramach tego samego wyrażenia musi być bezpośrednio zaangażowany w obliczanie wartości, która ma być zapisana .

Na przykład w i = i + 1 cały dostęp i (W L. H. S I W R. H. S) są bezpośrednio zaangażowane w Obliczanie wartości, która ma być zapisana. Więc jest dobrze.

Reguła ta skutecznie ogranicza wyrażenia prawne do tych, w których dostęp wyraźnie poprzedzają modyfikację.

Przykład 1:

std::printf("%d %d", i,++i); // invokes Undefined Behaviour because of Rule no 2

Przykład 2:

a[i] = i++ // or a[++i] = i or a[i++] = ++i etc

Jest niedozwolone, ponieważ jeden z dostępów i (ten w a[i]) nie ma nic wspólnego z wartością, która kończy się przechowywaniem w i (co dzieje się w i++), a więc nie ma dobrego sposobu na zdefiniowanie-zarówno dla naszego zrozumienia, jak i kompilatora-czy dostęp powinien mieć miejsce przed czy po zapisaniu zwiększonej wartości. Więc zachowanie jest nieokreślone.

Przykład 3:

int x = i + i++ ;// Similar to above

Kontynuacja odpowiedź dla C++11 tutaj .

 697
Author: Prasoon Saurav,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-06-15 03:00:09

Jest to kontynuacja mojej poprzedniej odpowiedzi i zawiera materiały związane z C++11..


Pre-requisites : elementarna wiedza o relacjach (Matematyka).


Czy to prawda, że w C++11 nie ma punktów sekwencji?

Tak!To prawda.

Punkty sekwencji zostały zastąpione przez uporządkowane przed i uporządkowane po (oraz Niezekwencjonowane oraz nieokreślona Sekwencja) relacje W C++11.


Czym dokładnie jest to 'uporządkowane wcześniej'?

Sequenced Before(§1.9/13) jest relacją, która jest:

Pomiędzy ocenami wykonywanymi przez pojedynczy wątek i indukuje ścisłe częściowe zamówienie1

Formalnie oznacza to, że biorąc pod uwagę dowolne dwie oceny (patrz niżej)A i B, Jeżeli A jest uporządkowane przed B, następnie wykonanie A poprzedzają wykonanie B. Jeśli A nie jest zsekwencjonowane przed B i B nie jest zsekwencjonowane przed A, to A i Bniezekwencjonowane 2.

Oceny A i Bnieokreślonymi sekwencjami , gdy albo {[2] } jest sekwencjonowane przed B lub B jest sekwencjonowane przed A, ale nie jest określone, które3.

[Przypisy]
1: ścisłym porządkiem częściowym jest relacja binarna "<" nad zbiorem P, który jest asymmetric, oraz transitive, czyli dla wszystkich a, b, i c W P mamy to:
........i). if a asymmetry);
........ii). if a transitivity).
2: wykonanie niezrównoważonych ocen może nakładać się.
3: indeterminately sequenced evaluations cannot overlap , but either could be executed first.


Jakie jest Znaczenie słowa 'ocena' w kontekście C++11?

W C++11 ocena wyrażenia (lub pod-wyrażenia) w ogólności obejmuje:
  • Obliczenia wartości (w tym określenie tożsamości obiektu dla glvalue evaluation i pobranie wartości wcześniej przypisanej do obiektu dla prvalue evaluation ) i

  • Rozpoczęcie działań niepożądanych .

Teraz (§1.9 / 14) mówi:

Każde obliczenie wartości i efekt uboczny związane z pełnym wyrażeniem jest uporządkowane przed każde obliczenie wartości i efekt uboczny związane z następne pełne wyrażenie do oceny .

  • Trywialny przykład:

    int x; x = 10; ++x;

    Obliczanie wartości i efekt uboczny związany z ++x jest sekwencjonowane po obliczeniu wartości i efekt uboczny x = 10;


Więc musi być jakiś związek między nieokreślonym zachowaniem a wyżej wymienionymi rzeczami, prawda?

Tak!Racja.

W (§1.9/15) wspomniano, że

Poza zaznaczeniem, oceny operandów poszczególnych operatorów i podwyrażeń poszczególnych wyrażeń są niezekwencjonowane4.

Na przykład:

int main()
{
     int num = 19 ;
     num = (num << 3) + (num >> 3);
} 
  1. ocena operandów + operatora nie jest równoważna względem siebie.
  2. ocena operandów << i >> operatorów nie jest równoważna względem siebie.

4: w wyrażeniu, które jest oceniane więcej niż raz podczas egzekucji z programu, unsequenced i indeterminately sequenced oceny jego podwyrażeń nie muszą być wykonywane konsekwentnie w różnych ocenach.

(§1.9/15) Obliczanie wartości operandów operatory są sekwencjonowane przed obliczeniem wartości wyniku operatora.

Oznacza to, że w x + y obliczanie wartości x i {[40] } są sekwencjonowane przed obliczeniem wartości z dnia (x + y).

Ważniejsze

(§1.9/15) jeśli efekt uboczny na obiekcie skalarnym nie jest równoważny względem

(A) inny efekt uboczny na tym samym obiekcie skalarnym

Lub

(B) obliczanie wartości przy użyciu wartości tego samego obiektu skalarnego.

Zachowanie jest nieokreślone.

Przykłady:

int i = 5, v[10] = { };
void  f(int,  int);
  1. i = i++ * ++i; // Undefined Behaviour
  2. i = ++i + i++; // Undefined Behaviour
  3. i = ++i + ++i; // Undefined Behaviour
  4. i = v[i++]; // Undefined Behaviour
  5. i = v[++i]: // Well-defined Behavior
  6. i = i++ + 1; // Undefined Behaviour
  7. i = ++i + 1; // Well-defined Behaviour
  8. ++++i; // Well-defined Behaviour
  9. f(i = -1, i = -1); // Undefined Behaviour (see below)

Podczas wywoływania funkcji (niezależnie od tego, czy funkcja jest inline, czy nie), każde obliczenie wartości i efekt uboczny związane z dowolnym wyrażeniem argumentu lub z wyrażeniem postfix oznaczającym wywołaną funkcję, jest sekwencjonowane przed wykonaniem każdego wyrażenia lub instrukcji w ciele wywołanej funkcji. [Uwaga: obliczenia wartości i efekty uboczne związane z różnymi wyrażeniami argumentów są niezekwencjonowane . - Uwaga końcowa ]

Wyrażenia (5), (7) i (8) nie wywołaj niezdefiniowanego zachowania. Sprawdź poniższe odpowiedzi, aby uzyskać bardziej szczegółowe wyjaśnienie.


Uwaga Końcowa :

Jeśli znajdziesz jakąś wadę w poście, zostaw komentarz. Power-użytkownicy (z rep >20000) nie wahaj się edytować posta za poprawianie literówek i innych błędów.

 280
Author: Prasoon Saurav,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:45

C++17 (N4659) zawiera propozycję dopracowania zlecenia oceny wyrażeń dla Idiomatic C++ która określa bardziej rygorystyczny porządek oceny wyrażeń.

W szczególnościnastępujące zdanie

8.18 operatory przypisania i przypisania złożonego:
....

We wszystkich przypadkach przypisanie jest sekwencjonowane po wartości obliczenie prawych i lewych operandów, a przed obliczeniem wartości wyrażenie przydziału. prawy operand jest sekwencjonowany przed lewym operandem.

Wraz z następującym wyjaśnieniem

Wyrażenie X {[15] } mówi się, że jest sekwencjonowane przed wyrażeniem Y jeśli każdy obliczanie wartości i każdy efekt uboczny związany z wyrażeniem X {[15] } jest sekwencjonowane przed każdą wartością obliczenie i każdy efekt uboczny związany z wyrażeniem Y .

Spraw, aby kilka przypadków wcześniej niezdefiniowanego zachowania było poprawnych, w tym ten, o którym mowa:

a[++i] = i;

Jednak kilka innych podobnych przypadków nadal prowadzi do nieokreślonego zachowania.

W N4140:

i = i++ + 1; // the behavior is undefined

Ale w N4659

i = i++ + 1; // the value of i is incremented
i = i++ + i; // the behavior is undefined

Oczywiście używanie kompilatora zgodnego z C++17 nie musi oznaczać, że należy zacząć pisać takie wyrażenia.

 32
Author: AlexD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-03-22 23:35:21

Zgaduję, że istnieje fundamentalny powód zmiany, nie jest to tylko kosmetyczne, aby stara interpretacja była jaśniejsza: ten powód jest współbieżny. Nieokreślona kolejność opracowania jest jedynie wyborem jednego z kilku możliwych seryjnych zleceń, różni się to zupełnie od zleceń przed i po, ponieważ jeśli nie ma określonego zlecenia, możliwa jest jednoczesna ocena: nie tak jest ze starymi zasadami. Na przykład w:

f (a,b)

Poprzednio albo a potem b, albo, b potem a. teraz, a i b mogą być oceniane za pomocą instrukcji przeplatanych lub nawet na różnych rdzeniach.

 11
Author: Yttrill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-07 19:44:51

W C99(ISO/IEC 9899:TC3), który wydaje się nieobecny w tej dyskusji do tej pory, są następujące steteenty dotyczące kolejności oceny.

[...] kolejność oceny podwyrażeń oraz kolejność, w jakiej działania niepożądane mają miejsce są zarówno nieokreślone. (Sekcja 6.5 str. 67)

Kolejność oceny operandów jest nieokreślona. Jeśli próba jest dokonywana w celu modyfikacji wyniku operatora przyporządkowania lub uzyskania do niego dostępu po kolejnym punkcie sekwencji zachowanie [sic] jest undefined.(Sekcja 6.5.16 s. 91)

 2
Author: awiebe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-26 03:40:09