An implementation can 1 reorder if this is not performed on function calls from other translation units.
This reordering is orthogonal to multithreaded, that is, it is performed both in single-threaded and in multithreaded programs.
If func2 is in the same translation block as func1, execution can be done like this:
func1 (struct *p) { func2 (p); p->a = x; }
Use volatile if you want to prevent 2 such reorders. (Note that this is done to prevent the reordering mentioned above, and not for other synchronization purposes. You will have to use atomic primitives for this.)
1 (Quoted from: ISO / IEC 9899: 201x 5.1.2.3 Program Execution 10)
Alternatively, an implementation can perform various optimizations within each translation unit, for example, that the actual semantics are consistent with abstract semantics only when making function calls across the boundaries of the translation unit.
2 (Quoted from: ISO / IEC 9899: 201x 6.7.3 Typical Classifiers 7)
An object that has a mutable type can be modified in ways that are unknown to the implementation or have other unknown side effects. Therefore, any expression that refers to such an object is evaluated strictly in accordance with the rules of an abstract machine, as described in 5.1.2.3. In addition, at each point in the sequence, the last value stored in the object agrees with what is prescribed by the abstract machine, with the exception of cases of unknown factors mentioned earlier.
source share