-
Notifications
You must be signed in to change notification settings - Fork 160
/
sequence_point.c
195 lines (145 loc) · 5.12 KB
/
sequence_point.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
/*
# Sequence point
- http://stackoverflow.com/questions/10782863/what-is-the-correct-answer-for-cout-c-c
- http://stackoverflow.com/questions/3575350/sequence-points-in-c
- http://stackoverflow.com/questions/4176328/undefined-behavior-and-sequence-points
Some language features force side effects of an expression evaluation
to happen before continuing while others don't.
A typical example of a side effect is the increment of `i` on `i++`,
being the main effect the returning of the value of `i` before the increment.
Sequence points are the operations which enforce order of evaulation.
A full list is given here: <http://en.wikipedia.org/wiki/Sequence_point>
Since operators such as `*` or `+` are not on the sequence point list,
it is undetermined behaviour in `f1() * f2()` if `f1` or `f2` is evaluated first.
The same goes for `-`, `/` and the evaulation of arguments of a function.
Other operators however do create sequence points, for example the comma operator.
In simple cases, gcc 4.7 is smart enough to notice undefiened behaviour and emmits a warning.
This warning has been ignored for test purposes by a test only `-Wno-sequence-point` flag.
# Order of evaulation of arguments
The order of evaluation for expressions that are arguments
of functions or operators is unspecified behaviour.
For example, it is unspecified behaviour in
`f1() * f2()` if `f1` or `f2` is evaluated first,
which may matter if they have side-effects.
The same goes for `g(f1(), f2())`.
Also note that there is a further separation between evaluation (getting the value to pass forward),
and side effects which this evaulation may have (increasing `i` on an `i++`).
See sequence point` for that.
TODO example.
*/
#include "common.h"
int global = 1;
int post_inc_global() {
global++;
return global - 1;
}
int main(void) {
/*
Implementation defined.
`=`does not create sequence points, to it is not possible to know if the side effect of `++`
will happen before or after the assigment.
Therefore, it is possible that this gives either:
post_increment_return = 0;
i = post_increment_return;
i++;
or
post_increment_return = 0;
i++;
i = post_increment_return;
*/
{
int i = 0;
i = i++;
printf("i = i++ = %d\n", i);
assert(i == 0 || i == 1);
}
/*
Defined behaviour because the return statement
guarantees that all side effects have happened.
Contrast with `i = i++`, which has no return statement.
*/
{
global = 0;
global = post_inc_global();
assert(global == 0);
}
/*
`+` does not specify a sequence point between its two sides.
Therefore, the side effect of either `++` (incrementing i) may or may not happen before the other `++` is evaluated.
This allows for the following outputs:
0 + 0 (neither side effect happened before the other)
1 + 0 or 0 + 1 (one side effect happened before the other)
*/
{
int i = 0;
int j = i++ + i++;
printf("i++ + i++ = %d\n", j);
assert(j == 0 || j == 1);
}
/*
Implementation defined.
The return statements are full expressions,
so they guarantee that either one or the other function call happens first.
However, it is not possible to know which side of `-` is evaulated first.
Ouptput depends on evaulation order, giving either:
0 - 1 = -1
if the first `post_inc_global()` is evaluated first or
1 - 0 = 1
if the second `post_inc_global()` is evaluated first.
*/
{
global = 0;
int j = post_inc_global() - post_inc_global();
printf("post_inc_global() - post_inc_global() = %d\n", j);
assert(j == -1 || j == 1);
}
/*
Defined behaviour because of the commutativity of addition and because the return statements guarantees
that all side effects are done with.
Constrast with `++global + ++global`, which is unspecified
because there are no return statements.
This may give either:
0 + 1 = 1
or
1 + 0 = 1
so both are the same by coincidence.
*/
{
global = 0;
assert(post_inc_global() + post_inc_global() == 1);
}
/*
Comma operator introduces a sequence point.
*/
{
int i = 0;
assert((i++, i++) == 1);
}
/* declarators in declarator sequence instroduce sequence points */
{
int i = 0, j = i;
assert(j == 0);
}
/*
Boolean operators `||` and `&&` introduce a sequence point.
*/
{
/*
The following happens:
- the first i++ returns 0
- since this is false, the second side is evaulated
- since `||` adds a sequence point, the increment side effect must happen
- i now equals one, and `||` evals true
*/
{
int i = 0;
assert(i++ || i);
}
/* same as for `||` */
{
int i = 1;
assert(! (i-- && i));
}
}
return EXIT_SUCCESS;
}