Im trying to understand what this code exactly print
int main(void) {
pid_t r = (fork() && fork()) || fork();
if (r) {
printf("a");
} else {
printf("b");
}
}
In particular I want to understand how many "a" and "b" will be printed. The first solution I found is: 3 "a" and 2 "b" but Im not really sure about it, by the way I thought this could be the explanation:
The first fork()
returns 0 to the child process (p1) and a non-zero value to the parent (p0) so the &&
operator for the children returns 0 and the shortcircuit for ||
is taken and it needs to be evaluated.
At this point p1 will fork()
a child process (p2) with the corresponding return values (0 for p1 and a non-zero for p2).
After this operations what follows is that the variable r
for p1 is a non-zero value while for p2 is 0.
This means that the program will print "a" and "b".
Heading back to p0, since the left part of &&
is non-zero values, it need to evaluate the right side, so there will be another fork()
that will return a non-zero value to p0 and 0 to the child process (p3).
Now we need to check what happens with the ||
operator:
||
r
= non-zero||
fork()
call, another child (p4) will be created with return value = 0 and the r
value for p4 will also be 0, while the r
value for p3 will be a non-zero, so the print is "a" and "b".I know that this kind of question has already received a reply but I just want to be sure that my reasoning makes sense.
What we know:
man fork
: fork()
shall return 0 to the child process and shall return the process ID of the child process to the parent process.||
and &&
are short-circuited.&&
does not execute the right side when the left side is nonzero.||
does not execute the right side when the left side is zero.||
is true when one of the sides is nonzero.&&
is true when both sides are nonzero.Ok what happens:
pid_t r = (fork() && fork()) || fork();
Let's summarize that into a table. Each r =
line one fork()
is in bold - it means this fork()
will be executed. When a child has fork() = 0
that means it is "spawned" - a parent has child's PID then as the return value of fork()
.
stage | main process | child1 | child2 | child3 | child4 |
---|---|---|---|---|---|
r = | (fork() && fork()) || fork() | ||||
fork()= | child1pid | 0 | |||
r = | (child1pid && fork()) || fork() | (0 && ignored) || fork() | |||
fork()= | child2pid | child3pid | 0 | 0 | |
r = | (child1pid && child2pid) || ignored | (0 && ignored) || childpid3 | (child1pid && 0) || fork() | (0 && ignored) || 0 | |
fork()= | child4pid | 0 | |||
r = | (child1pid && child2pid) || ignored | (0 && ignored) || childpid3 | (child1pid && 0) || child4pid | (0 && ignored) || 0 | (child1pid && 0) || 0 |
r = | true || ignored | false || childpid3 | false || child4pid | false || 0 | false || 0 |
r = | true | true | true | false | false |
a | a | a | b | b |
3 times a
and 2 times b
will be printed.