Welcome to Software Development on Codidact!
Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.
Post History
These examples have undefined behavior and unspecified behavior all at once! Operator precedence has nothing to do with the order of execution, see What is the difference between operator precedenc...
Answer
#3: Post edited
- _**These examples have undefined behavior and unspecified behavior all at once!**_
- Operator precedence has nothing to do with the order of execution, see [What is the difference between operator precedence and order of evaluation?](https://software.codidact.com/questions/278172) From that post we can also learn that most of the above examples have _unspecified behavior_, since they rely on a certain order of evaluation. This is true even for the assignment operator, which only guarantees that the value storage happens after evaluation of left and right operands, but not in which order the operands are evaluated.
- But aside for the unspecified order of evaluation, these examples have an even more fundamental problem - they also have _undefined behavior_, which I will address further below.
- ---
- **_Side effects and sequence points_**
- There are two formal C standard terms we need to understand, to get to the bottom of understanding all this: _side effect_ and _sequence point_.
- - Side effects are defined in the C standard as (C17 5.1.2.3):
- > Accessing a volatile object, modifying an object, modifying a file, or calling a function
- that does any of those operations are all _side effects_, which are changes in the state of
- the execution environment. Evaluation of an expression in general includes both value
- computations and initiation of side effects.
- In plain English: updating the value of a variable is a side effect.
- - Sequence points are best described in an older version of the standard (C99 5.2.1.3):
- > At certain specified points in the execution sequence called _sequence points_, all side effects
- of previous evaluations shall be complete and no side effects of subsequent evaluations
- shall have taken place.
- So sequence points are places where the compiler must be done with all previous execution. The most obvious example of a sequence point is the `;` semicolon at the end of an expression.
- This means that when we have code like this:
- something();
- i = i++ + ++i;
- then the two `;` are the sequence points. Everything between them does not have a well-defined execution order, the operands are _unsequenced_. This is problematic if we place one or several side effects there, because then the compiler doesn't necessarily know in which order it is expected to read and update each variable. And this (horrible) code has no less than 3 side effects: the updating of `i` as result of assignment, the `i++` and the `++i`.
- ---
- **_Undefined behavior and sequence points_**
- The C standard states that all such expressions are _undefined behavior_, see [What is undefined behavior and how does it work?](https://software.codidact.com/questions/277486) Meaning not only compiler-specific, but also a potential subtle but severe bug and there are no deterministic or guaranteed results. The C99 version of the standard expresses it in the most readable way (C99 6.5/2):
- > Between the previous and next sequence point an object shall have its stored value
- modified at most once by the evaluation of an expression. Furthermore, the prior value
- shall be read only to determine the value to be stored. <sup>73)</sup>
- (Latter standards use a much more confusing wording "sequenced before" and "sequenced after", but the meaning is the same.)
- From the above quote we can learn that it is also undefined behavior to use the same variable at several times in an expression, for unrelated purposes and when there are side effects present. Note 73 gives some examples:
- > <sup>73)</sup> This paragraph renders undefined statement expressions such as
- >
- > `i = ++i + 1;`
- > `a[i++] = i;`
- >
- > while allowing
- >
- > `i = i + 1;`
- > `a[i] = i;`
- In the latter well-defined cases, the variable `i` occurs at several times in the same expression, but with at most one side effect and only for the purpose of calculating which value to store.
- ---
- **_How to fix all these expressions, so that they are well-defined, portable and safe_**
- The best and toughest rule of thumb (recommended by yours sincerely) is to never mix `++` or `--` operators with other operators at all, ever. If we stick to that rule, we also don't need to ponder the difference between `++i` and `i++`, since prefix vs postfix only matters when those operators are mixed with other operands.
- A slightly more lenient rule could be to follow the MISRA-C guidelines for the use of C in critical systems (an industry standard, see [MISRA-C](https://www.misra.org.uk/Activities/MISRAC/tabid/160/Default.aspx)), which has this rule (MISRA-C:2012 Rule 13.3):
- > A full expression containing an increment (++) or decrement (--) operator should have no other potential _side effects_ other than that caused by the increment or decrement operator.
- This still allows us to mix these operators with others, as long as we are aware that there are no other side effects present, so it is a quite reasonable rule.
- In addition, we should never write code that relies on the unspecified order of evaluation. Again, MISRA-C provides a sensible rule (MISRA-C:2012 Rule 13.2):
- > The value of an expression and its _persistent side effects_ shall be the same under all permitted evaluation orders
Meaning that writing code containing unspecified order of evaluation is fine, as long as the results does not depend on that order and we get the same results no matter compiler.
- _**These examples have undefined behavior and unspecified behavior all at once!**_
- Operator precedence has nothing to do with the order of execution, see [What is the difference between operator precedence and order of evaluation?](https://software.codidact.com/questions/278172) From that post we can also learn that most of the above examples have _unspecified behavior_, since they rely on a certain order of evaluation. This is true even for the assignment operator, which only guarantees that the value storage happens after evaluation of left and right operands, but not in which order the operands are evaluated.
- But aside for the unspecified order of evaluation, these examples have an even more fundamental problem - they also have _undefined behavior_, which I will address further below.
- ---
- **_Side effects and sequence points_**
- There are two formal C standard terms we need to understand, to get to the bottom of understanding all this: _side effect_ and _sequence point_.
- - Side effects are defined in the C standard as (C17 5.1.2.3):
- > Accessing a volatile object, modifying an object, modifying a file, or calling a function
- that does any of those operations are all _side effects_, which are changes in the state of
- the execution environment. Evaluation of an expression in general includes both value
- computations and initiation of side effects.
- In plain English: updating the value of a variable is a side effect.
- - Sequence points are best described in an older version of the standard (C99 5.2.1.3):
- > At certain specified points in the execution sequence called _sequence points_, all side effects
- of previous evaluations shall be complete and no side effects of subsequent evaluations
- shall have taken place.
- So sequence points are places where the compiler must be done with all previous execution. The most obvious example of a sequence point is the `;` semicolon at the end of an expression.
- This means that when we have code like this:
- something();
- i = i++ + ++i;
- then the two `;` are the sequence points. Everything between them does not have a well-defined execution order, the operands are _unsequenced_. This is problematic if we place one or several side effects there, because then the compiler doesn't necessarily know in which order it is expected to read and update each variable. And this (horrible) code has no less than 3 side effects: the updating of `i` as result of assignment, the `i++` and the `++i`.
- ---
- **_Undefined behavior and sequence points_**
- The C standard states that all such expressions are _undefined behavior_, see [What is undefined behavior and how does it work?](https://software.codidact.com/questions/277486) Meaning not only compiler-specific, but also a potential subtle but severe bug and there are no deterministic or guaranteed results. The C99 version of the standard expresses it in the most readable way (C99 6.5/2):
- > Between the previous and next sequence point an object shall have its stored value
- modified at most once by the evaluation of an expression. Furthermore, the prior value
- shall be read only to determine the value to be stored. <sup>73)</sup>
- (Latter standards use a much more confusing wording "sequenced before" and "sequenced after", but the meaning is the same.)
- From the above quote we can learn that it is also undefined behavior to use the same variable at several times in an expression, for unrelated purposes and when there are side effects present. Note 73 gives some examples:
- > <sup>73)</sup> This paragraph renders undefined statement expressions such as
- >
- > `i = ++i + 1;`
- > `a[i++] = i;`
- >
- > while allowing
- >
- > `i = i + 1;`
- > `a[i] = i;`
- In the latter well-defined cases, the variable `i` occurs at several times in the same expression, but with at most one side effect and only for the purpose of calculating which value to store.
- ---
- **_How to fix all these expressions, so that they are well-defined, portable and safe_**
- The best and toughest rule of thumb (recommended by yours sincerely) is to never mix `++` or `--` operators with other operators at all, ever. If we stick to that rule, we also don't need to ponder the difference between `++i` and `i++`, since prefix vs postfix only matters when those operators are mixed with other operands.
- A slightly more lenient rule could be to follow the MISRA-C guidelines for the use of C in critical systems (an industry standard, see [MISRA-C](https://www.misra.org.uk/Activities/MISRAC/tabid/160/Default.aspx)), which has this rule (MISRA-C:2012 Rule 13.3):
- > A full expression containing an increment (++) or decrement (--) operator should have no other potential _side effects_ other than that caused by the increment or decrement operator.
- This still allows us to mix these operators with others, as long as we are aware that there are no other side effects present, so it is a quite reasonable rule.
- In addition, we should never write code that relies on the unspecified order of evaluation. Again, MISRA-C provides a sensible rule (MISRA-C:2012 Rule 13.2):
- > The value of an expression and its _persistent side effects_ shall be the same under all permitted evaluation orders
- Meaning that writing code containing unspecified order of evaluation is fine, as long as the results don't depend on that order and we get the same results no matter compiler.
#2: Post edited
- _**These examples have undefined behavior and unspecified behavior all at once!**_
Operator precedence has nothing to do with the order of execution, see [What is the difference between operator precedence and order of evaluation?](https://software.codidact.com/questions/278172). From that post we can also learn that most of the above examples have _unspecified behavior_, since they rely on a certain order of evaluation. This is true even for the assignment operator, which only guarantees that the value storage happens after evaluation of left and right operands, but not in which order the operands are evaluated.But aside for the unspecified order of evaluation, these examples have an even more fundamental problem - they also have _undefined behavior_, which I will address below.- ---
- **_Side effects and sequence points_**
There are two formal C standard terms we need to understand to get to the bottom of understanding all this: _side effect_ and _sequence point_. Side effects are defined in the C standard as (C17 5.1.2.3):> Accessing a volatile object, modifying an object, modifying a file, or calling a function- that does any of those operations are all _side effects_, which are changes in the state of
- the execution environment. Evaluation of an expression in general includes both value
- computations and initiation of side effects.
In plain English: updating the value of a variable is a side effect.Sequence points are best described in an older version of the standard (C99 5.2.1.3):> At certain specified points in the execution sequence called _sequence points_, all side effects- of previous evaluations shall be complete and no side effects of subsequent evaluations
- shall have taken place.
So sequence points are places where the compiler must be done with all previous execution. The most obvious example of a sequence point is the `;` semicolon at the end of an expression. This means that when we have code like this:- something();
- i = i++ + ++i;
then the two `;` are the sequence points. Everything between them does not have a well-defined execution order, the operands are _unsequenced_. This is problematic if we place one or several side effects there, because then the compiler doesn't necessarily know in which order it is expected to read and update each variable.- ---
- **_Undefined behavior and sequence points_**
The C standard states that all such expressions are _undefined behavior_, see [What is undefined behavior and how does it work?](https://software.codidact.com/questions/277486) Meaning not only compiler-specific, but also a potential subtle but severe bug. The C99 version of the standard expresses it in the most readable way (C99 6.5/2):- > Between the previous and next sequence point an object shall have its stored value
- modified at most once by the evaluation of an expression. Furthermore, the prior value
- shall be read only to determine the value to be stored. <sup>73)</sup>
- (Latter standards use a much more confusing wording "sequenced before" and "sequenced after", but the meaning is the same.)
- From the above quote we can learn that it is also undefined behavior to use the same variable at several times in an expression, for unrelated purposes and when there are side effects present. Note 73 gives some examples:
> 73) This paragraph renders undefined statement expressions such as- >
- > `i = ++i + 1;`
- > `a[i++] = i;`
- >
- > while allowing
- >
- > `i = i + 1;`
- > `a[i] = i;`
- In the latter well-defined cases, the variable `i` occurs at several times in the same expression, but with at most one side effect and only for the purpose of calculating which value to store.
- ---
- **_How to fix all these expressions, so that they are well-defined, portable and safe_**
The best and toughest rule of thumb (recommended by yours sincerely) is to never mix `++` or `--` operators with other operators at all, ever.If we stick to that rule, we also don't need to ponder the difference between `++i` and `i++`, since prefix vs postfix only matters when those operators are mixed with other operands.- A slightly more lenient rule could be to follow the MISRA-C guidelines for the use of C in critical systems (an industry standard, see [MISRA-C](https://www.misra.org.uk/Activities/MISRAC/tabid/160/Default.aspx)), which has this rule (MISRA-C:2012 Rule 13.3):
- > A full expression containing an increment (++) or decrement (--) operator should have no other potential _side effects_ other than that caused by the increment or decrement operator.
- This still allows us to mix these operators with others, as long as we are aware that there are no other side effects present, so it is a quite reasonable rule.
- In addition, we should never write code that relies on the unspecified order of evaluation. Again, MISRA-C provides a sensible rule (MISRA-C:2012 Rule 13.2):
- > The value of an expression and its _persistent side effects_ shall be the same under all permitted evaluation orders
- Meaning that writing code containing unspecified order of evaluation is fine, as long as the results does not depend on that order and we get the same results no matter compiler.
- _**These examples have undefined behavior and unspecified behavior all at once!**_
- Operator precedence has nothing to do with the order of execution, see [What is the difference between operator precedence and order of evaluation?](https://software.codidact.com/questions/278172) From that post we can also learn that most of the above examples have _unspecified behavior_, since they rely on a certain order of evaluation. This is true even for the assignment operator, which only guarantees that the value storage happens after evaluation of left and right operands, but not in which order the operands are evaluated.
- But aside for the unspecified order of evaluation, these examples have an even more fundamental problem - they also have _undefined behavior_, which I will address further below.
- ---
- **_Side effects and sequence points_**
- There are two formal C standard terms we need to understand, to get to the bottom of understanding all this: _side effect_ and _sequence point_.
- - Side effects are defined in the C standard as (C17 5.1.2.3):
- > Accessing a volatile object, modifying an object, modifying a file, or calling a function
- that does any of those operations are all _side effects_, which are changes in the state of
- the execution environment. Evaluation of an expression in general includes both value
- computations and initiation of side effects.
- In plain English: updating the value of a variable is a side effect.
- - Sequence points are best described in an older version of the standard (C99 5.2.1.3):
- > At certain specified points in the execution sequence called _sequence points_, all side effects
- of previous evaluations shall be complete and no side effects of subsequent evaluations
- shall have taken place.
- So sequence points are places where the compiler must be done with all previous execution. The most obvious example of a sequence point is the `;` semicolon at the end of an expression.
- This means that when we have code like this:
- something();
- i = i++ + ++i;
- then the two `;` are the sequence points. Everything between them does not have a well-defined execution order, the operands are _unsequenced_. This is problematic if we place one or several side effects there, because then the compiler doesn't necessarily know in which order it is expected to read and update each variable. And this (horrible) code has no less than 3 side effects: the updating of `i` as result of assignment, the `i++` and the `++i`.
- ---
- **_Undefined behavior and sequence points_**
- The C standard states that all such expressions are _undefined behavior_, see [What is undefined behavior and how does it work?](https://software.codidact.com/questions/277486) Meaning not only compiler-specific, but also a potential subtle but severe bug and there are no deterministic or guaranteed results. The C99 version of the standard expresses it in the most readable way (C99 6.5/2):
- > Between the previous and next sequence point an object shall have its stored value
- modified at most once by the evaluation of an expression. Furthermore, the prior value
- shall be read only to determine the value to be stored. <sup>73)</sup>
- (Latter standards use a much more confusing wording "sequenced before" and "sequenced after", but the meaning is the same.)
- From the above quote we can learn that it is also undefined behavior to use the same variable at several times in an expression, for unrelated purposes and when there are side effects present. Note 73 gives some examples:
- > <sup>73)</sup> This paragraph renders undefined statement expressions such as
- >
- > `i = ++i + 1;`
- > `a[i++] = i;`
- >
- > while allowing
- >
- > `i = i + 1;`
- > `a[i] = i;`
- In the latter well-defined cases, the variable `i` occurs at several times in the same expression, but with at most one side effect and only for the purpose of calculating which value to store.
- ---
- **_How to fix all these expressions, so that they are well-defined, portable and safe_**
- The best and toughest rule of thumb (recommended by yours sincerely) is to never mix `++` or `--` operators with other operators at all, ever. If we stick to that rule, we also don't need to ponder the difference between `++i` and `i++`, since prefix vs postfix only matters when those operators are mixed with other operands.
- A slightly more lenient rule could be to follow the MISRA-C guidelines for the use of C in critical systems (an industry standard, see [MISRA-C](https://www.misra.org.uk/Activities/MISRAC/tabid/160/Default.aspx)), which has this rule (MISRA-C:2012 Rule 13.3):
- > A full expression containing an increment (++) or decrement (--) operator should have no other potential _side effects_ other than that caused by the increment or decrement operator.
- This still allows us to mix these operators with others, as long as we are aware that there are no other side effects present, so it is a quite reasonable rule.
- In addition, we should never write code that relies on the unspecified order of evaluation. Again, MISRA-C provides a sensible rule (MISRA-C:2012 Rule 13.2):
- > The value of an expression and its _persistent side effects_ shall be the same under all permitted evaluation orders
- Meaning that writing code containing unspecified order of evaluation is fine, as long as the results does not depend on that order and we get the same results no matter compiler.
#1: Initial revision
_**These examples have undefined behavior and unspecified behavior all at once!**_ Operator precedence has nothing to do with the order of execution, see [What is the difference between operator precedence and order of evaluation?](https://software.codidact.com/questions/278172). From that post we can also learn that most of the above examples have _unspecified behavior_, since they rely on a certain order of evaluation. This is true even for the assignment operator, which only guarantees that the value storage happens after evaluation of left and right operands, but not in which order the operands are evaluated. But aside for the unspecified order of evaluation, these examples have an even more fundamental problem - they also have _undefined behavior_, which I will address below. --- **_Side effects and sequence points_** There are two formal C standard terms we need to understand to get to the bottom of understanding all this: _side effect_ and _sequence point_. Side effects are defined in the C standard as (C17 5.1.2.3): > Accessing a volatile object, modifying an object, modifying a file, or calling a function that does any of those operations are all _side effects_, which are changes in the state of the execution environment. Evaluation of an expression in general includes both value computations and initiation of side effects. In plain English: updating the value of a variable is a side effect. Sequence points are best described in an older version of the standard (C99 5.2.1.3): > At certain specified points in the execution sequence called _sequence points_, all side effects of previous evaluations shall be complete and no side effects of subsequent evaluations shall have taken place. So sequence points are places where the compiler must be done with all previous execution. The most obvious example of a sequence point is the `;` semicolon at the end of an expression. This means that when we have code like this: something(); i = i++ + ++i; then the two `;` are the sequence points. Everything between them does not have a well-defined execution order, the operands are _unsequenced_. This is problematic if we place one or several side effects there, because then the compiler doesn't necessarily know in which order it is expected to read and update each variable. --- **_Undefined behavior and sequence points_** The C standard states that all such expressions are _undefined behavior_, see [What is undefined behavior and how does it work?](https://software.codidact.com/questions/277486) Meaning not only compiler-specific, but also a potential subtle but severe bug. The C99 version of the standard expresses it in the most readable way (C99 6.5/2): > Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be read only to determine the value to be stored. <sup>73)</sup> (Latter standards use a much more confusing wording "sequenced before" and "sequenced after", but the meaning is the same.) From the above quote we can learn that it is also undefined behavior to use the same variable at several times in an expression, for unrelated purposes and when there are side effects present. Note 73 gives some examples: > 73) This paragraph renders undefined statement expressions such as > > `i = ++i + 1;` > `a[i++] = i;` > > while allowing > > `i = i + 1;` > `a[i] = i;` In the latter well-defined cases, the variable `i` occurs at several times in the same expression, but with at most one side effect and only for the purpose of calculating which value to store. --- **_How to fix all these expressions, so that they are well-defined, portable and safe_** The best and toughest rule of thumb (recommended by yours sincerely) is to never mix `++` or `--` operators with other operators at all, ever. If we stick to that rule, we also don't need to ponder the difference between `++i` and `i++`, since prefix vs postfix only matters when those operators are mixed with other operands. A slightly more lenient rule could be to follow the MISRA-C guidelines for the use of C in critical systems (an industry standard, see [MISRA-C](https://www.misra.org.uk/Activities/MISRAC/tabid/160/Default.aspx)), which has this rule (MISRA-C:2012 Rule 13.3): > A full expression containing an increment (++) or decrement (--) operator should have no other potential _side effects_ other than that caused by the increment or decrement operator. This still allows us to mix these operators with others, as long as we are aware that there are no other side effects present, so it is a quite reasonable rule. In addition, we should never write code that relies on the unspecified order of evaluation. Again, MISRA-C provides a sensible rule (MISRA-C:2012 Rule 13.2): > The value of an expression and its _persistent side effects_ shall be the same under all permitted evaluation orders Meaning that writing code containing unspecified order of evaluation is fine, as long as the results does not depend on that order and we get the same results no matter compiler.