&&
has higher precedence than ||
, so
fork() && fork()||fork();
is parsed the same as
( fork() && fork() ) || fork();
(despite the suspicious spacing implying otherwise).
Short-circuit boolean algrebra dictates the following equivalencies
r = p() || q(); ⇔ r = p(); if ( !r ) r = q();
r = p() && q(); ⇔ r = p(); if ( r ) r = q();
(Not truly equivalent since &&
and ||
always return 0
or 1
, whereas my version returns 0
or a non-zero value. But that's good enough for us.)
As such,
( fork() && fork() ) || fork();
is equivalent to
pid_t r = fork();
if ( r ) r = fork();
if ( !r ) r = fork();
Finally, fork
returns zero in the child and non-zero in the parent (ignoring errors).
We have all that we need to work out the flow of the program.
Let's annotate the lines.
fork(); // A
pid_t r = fork(); // B
if ( r ) r = fork(); // C
if ( !r ) r = fork(); // D
fork(); // E
printf("YES"); // F
A B C D E F
main...fork....fork....fork............fork 1
| | v child 2
| | child...fork....fork 3
| | v child 4
| | child...fork 5
| v child 6
| child...........fork....fork 7
| v child 8
| child...fork 9
v child 10
child...fork....fork............fork 11
| v child 12
| child...fork....fork 13
| v child 14
| child...fork 15
v child 16
child...........fork....fork 17
v child 18
child...fork 19
child 20