C Language
Punkty sekwencyjne
Szukaj…
Uwagi
Międzynarodowa norma ISO / IEC 9899: 201x Języki programowania - C
Dostęp do lotnego obiektu, modyfikacja obiektu, modyfikacja pliku lub wywołanie funkcji wykonującej którąkolwiek z tych operacji to wszelkie skutki uboczne , które są zmianami w stanie środowiska wykonawczego.
Obecność punktu sekwencyjnego między oceną wyrażeń A i B oznacza, że każde obliczenie wartości i efekt uboczny związany z A jest sekwencjonowane przed każdym obliczeniem wartości i efektem ubocznym związanym z B.
Oto pełna lista punktów sekwencji z załącznika C internetowego projektu przed publikacją w 2011 r. Standardu języka C:
Punkty sekwencji
1 Poniżej przedstawiono punkty sekwencji opisane w 5.1.2.3:
- Między ocenami desygnatora funkcji a rzeczywistymi argumentami w wywołaniu funkcji i rzeczywistym wywołaniem. (6.5.2.2).
- Między ocenami pierwszego i drugiego argumentu następujących operatorów: logiczne AND
&&
(6.5.13); logiczne OR||
(6.5.14); przecinek,
(6.05.17).- Między ocenami pierwszego argumentu warunkowego
? :
operator i cokolwiek z drugiego i trzeciego argumentu jest oceniane (6.5.15).- Koniec pełnego deklaratora: deklaratory (6.7.6);
- Między oceną pełnego wyrażenia a następnym pełnym wyrażeniem do oceny. Oto pełne wyrażenia: inicjator, który nie jest częścią literału złożonego (6.7.9); wyrażenie w wyrażeniu wyrażeniowym (6.8.3); kontrolne wyrażenie instrukcji wyboru (
if
lubswitch
) (6.8.4); kontrolujące wyrażenie instrukcjiwhile
lubdo
(6.8.5); każde (opcjonalne) wyrażenie instrukcjifor
(6.8.5.3); (opcjonalne) wyrażenie w instrukcjireturn
(6.8.6.4).- Bezpośrednio przed powrotem funkcji biblioteki (7.1.4).
- Po działaniach związanych z każdym sformatowanym specyfikatorem konwersji funkcji wejścia / wyjścia (7.21.6, 7.29.2).
- Bezpośrednio przed i bezpośrednio po każdym wywołaniu funkcji porównawczej, a także między każdym wywołaniem funkcji porównawczej a każdym ruchem obiektów przekazywanych jako argumenty do tego wywołania (7.22.5).
Wyrażenia sekwencyjne
Sekwencjonowane są następujące wyrażenia:
a && b
a || b
a , b
a ? b : c
for ( a ; b ; c ) { ... }
We wszystkich przypadkach wyrażenie a
jest w pełni oceniane i wszystkie działania niepożądane są stosowane przed oceną b
lub c
. W czwartym przypadku oceniany będzie tylko jeden z b
lub c
. W ostatnim przypadku b
jest w pełni oceniane, a wszystkie działania niepożądane są stosowane przed oceną c
.
We wszystkich przypadkach ocena ekspresji a
jest sekwencjonowana przed ocenami b
lub c
(alternatywnie oceny b
i c
są sekwencjonowane po ocenie a
).
Tak więc wyrażenia takie jak
x++ && x++
x++ ? x++ : y++
(x = f()) && x != 0
for ( x = 0; x < 10; x++ ) { ... }
y = (x++, x++);
mają dobrze określone zachowanie.
Wyrażenia niesekwencjonowane
Następujące wyrażenia nie mają konsekwencji :
a + b;
a - b;
a * b;
a / b;
a % b;
a & b;
a | b;
W powyższych przykładach wyrażenie a
może być ocenione przed lub po wyrażeniu b
, b
może być ocenione przed a
, lub nawet mogą być zmieszane, jeśli odpowiadają kilku instrukcjom.
Podobna zasada dotyczy wywołań funkcji:
f(a, b);
Tutaj nie tylko i a
b
są unsequenced (to znaczy ,
operator wywołania funkcji wywołuje punkt sekwencji), a także f
, ekspresji, który określa funkcję, która ma być wykorzystane.
Efekty uboczne można zastosować natychmiast po ocenie lub odłożyć na później.
Wyrażenia takie jak
x++ & x++;
f(x++, x++); /* the ',' in a function call is *not* the same as the comma operator */
x++ * x++;
a[i] = i++;
lub
x++ & x;
f(x++, x);
x++ * x;
a[i++] = i;
spowoduje niezdefiniowane zachowanie, ponieważ
- modyfikacja obiektu i każdy inny dostęp do niego muszą być zsekwencjonowane
- kolejność oceny i kolejność stosowania efektów ubocznych 1 nie jest określona.
1 Wszelkie zmiany stanu środowiska wykonawczego.
Wyrażenia nieokreślone sekwencyjnie
Wywołania funkcyjne jako f(a)
zawsze oznaczają punkt sekwencyjny między oceną argumentów i desygnatora (tutaj f
i a
) a rzeczywistym wywołaniem. Jeśli dwa takie wywołania nie są konsekwentne, dwa wywołania funkcji są sekwencyjnie nieokreślone, to znaczy jedno jest wykonywane przed drugim, a kolejność jest nieokreślona.
unsigned counter = 0;
unsingned account(void) {
return counter++;
}
int main(void) {
printf("the order is %u %u\n", account(), account());
}
Ta niejawna podwójna modyfikacja counter
podczas oceny argumentów printf
jest poprawna, po prostu nie wiemy, które z wywołań jest pierwsze. Ponieważ zamówienie nie jest określone, może się różnić i nie można na nim polegać. Tak więc wydruk może być:
kolejność wynosi 0 1
lub
kolejność wynosi 1 0
Analogiczne oświadczenie do powyższego bez wywołania funkcji pośredniej
printf("the order is %u %u\n", counter++, counter++); // undefined behavior
ma niezdefiniowane zachowanie, ponieważ nie ma punktu sekwencji między dwiema modyfikacjami counter
.