A sequence point in programming is any point in a program where it is guaranteed that the side effects of all the previous calculations have already emerged while there are no side effects of the following calculations yet.
Sequence points are often mentioned when speaking of C and C++ languages since in these languages it is especially simple to write an expression whose value might depend on an undefined order of side effects' emergence. Adding one or several sequence points determines the order more strictly and is one of the methods for achieving a stable (i.e. correct) result.
Sequence points are necessary when one and the same variable is modified more than once in a single expression. The expression i=i++ is often given as an example in which the 'i' variable is being assigned to and incremented at the same time. What value will the 'i' variable posses? The language standard must either define one of the possible program behaviors as the only correct one, define a range of behaviors which are correct, or specify that the program's behavior is completely undefined in such a case. In C and C++, calculation of the i=i++ expression causes an undefined behavior since this expression does not contain any sequence points inside.
The following sequence points are defined in C and C++:
Now let's examine several examples causing undefined behavior:
int i, j;
...
X[i]=++i;
X[i++] = i;
j = i + X[++i];
i = 6 + i++ + 2000;
j = i++ + ++i;
i = ++i + ++i;
In all these cases you cannot predict the calculations' results. Of course, these samples are artificial and you can see the danger at the first glance, so consider a code fragment found by the PVS-Studio analyzer in a real-life application:
while (!(m_pBitArray[m_nCurrentBitIndex >> 5] &
Powers_of_Two_Reversed[m_nCurrentBitIndex++ & 31]))
{}
return (m_nCurrentBitIndex - BitInitial - 1);
The compiler could calculate any (left or right) argument of the '&' operator first. It means that the m_nCurrentBitIndex variable will or will not be incremented by one when calculating "m_pBitArray[m_nCurrentBitIndex >> 5]".
This code can work correctly for a long time. But you should keep in mind that its correct operation is guaranteed only until it is built with a particular compiler's version with an invariable set of compilation parameters. This is the correct code:
while (!(m_pBitArray[m_nCurrentBitIndex >> 5] &
Powers_of_Two_Reversed[m_nCurrentBitIndex & 31]))
{ ++m_nCurrentBitIndex; }
return (m_nCurrentBitIndex - BitInitial);
This code doesn't contain ambiguities anymore. At the same time we got rid of the magic constant "-1".
Programmers often think that undefined behavior might occur only when using postincrement, while preincrement is safe. It's not true. Consider an example from a discussion on this topic.
Question:
I downloaded a demo version of PVS-Studio, ran it on my project and got the following warning: V567 Undefined behavior. The 'i_acc' variable is modified while being used twice between sequence points.
Code
i_acc = (++i_acc) % N_acc;
It seems to me there's no undefined behavior here because the i_acc variable doesn't participate in the expression twice.
Answer:
There is undefined behavior here. It's another thing that the probability of error occurrence is very low in this case. The '=' operator is not a sequence point. It means that the compiler might place the i_acc variable's value into the register first and then increment it in the register. After that it will calculate the expression and write the result into the i_acc variable. Then it will again write the incremented value from the register into the variable. The resulting code will look like this:
REG = i_acc;
REG++;
i_acc = (REG) % N_acc;
i_acc = REG;
The compiler has a full right to do this. Of course, in practice it most likely will increment the variable at once and everything will work as the programmer expects. But you should not rely on that.
0