1

In C, the fork() function will return a non zero value for the parent process and zero for the child process.

When working with logical expression such as && or ||, it confused me when trying to know how those codes work. How do I show them in tree by graph theory? How the codes were executed?

#include <stdio.h> 
#include <unistd.h> 
int main() 
{ 
   fork(); 
   fork() && fork(); // or fork() || fork() 

   printf("forked\n"); 
   return 0; 
} 
Huy
  • 191
  • 6
  • 18
  • 1
    As for your problem please think about the [short-circuit evaluation](https://en.wikipedia.org/wiki/Short-circuit_evaluation) used by the logical operator `&&` and `||`, and a little bit more what [`fork`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html) returns, and what "false" and "true" values there are. – Some programmer dude Nov 04 '19 at 08:50
  • `fork() && fork()`: Consider that `&&` is short-circuit. The process which executes this, calls both. Its first child process (which receives 0 for the first) doesn't execute the second. (First and second child processes continue with `printf()`.) – Scheff's Cat Nov 04 '19 at 08:50
  • `fork()` can also return -1 (nonzero) on an error, so be sure to consider that as well. – Steve Friedl Nov 04 '19 at 14:59

2 Answers2

4

Both logical AND (&&) and logical OR (||) operator employs short-circuiting behavior.

expr1 && expr2: expr2 is not evaluated if expr1 is logical 0 (false).
expr1 || expr2: expr2 is not evaluated if expr1 is logical 1 (true).

With logical short-circuiting, the second operand, expr2, is evaluated only when the result is not fully determined by the first operand, expr1.

Assuming all the fork() calls in parent as well as child process are result in success, the tree of forked processes will be like this:

 parent process
     |
     |
   fork()    // first fork
   -----     // below in the tree, RV is fork() returned value
     |
     |----child process [RV: 0]--
     |                          |
   [RV: child PID]              |
     |                          |
   fork() && fork()            fork() && fork()
   ------                      ------
     |                          |
     |                          |--child process [RV: 0]--
     |                          |                        |
     |                         [RV: X (child PID)]       |
     |                          |                      0 && fork()   
     |                          |                   // due to && short-circuiting behavior, 
     |                          |                   // fork() will not be called
     |                          |                        |
     |                          |                       Print "forked"
     |                          |                       Exit
     |                          |
     |                          |
     |                    X && fork()   // X is a non zero value and hence the fork() will be called
     |                         ------
     |                          |
     |                          |--child process [RV: 0]--
     |                          |                        |
     |                        [RV: child PID]            |
     |                          |                        |
     |                         Print "forked"           Print "forked"
     |                         Exit                     Exit
     |
     |--child process [RV: 0]--
     |                        |
   [RV: X (child PID)]        |
     |                      0 && fork()
     |                      // due to && short-circuiting behavior, fork() will not be called
     |                        |
     |                        |
     |                      Print "forked"
     |                      Exit
     |
X && fork()   // X is a non zero value and hence the fork() will be called
     ------
     |
     |--child process [RV: 0]--
     |                        |
   [RV: child PID]            |
     |                        |
     |                        |
   Print "forked"           Print "forked"
   Exit                     Exit

I hope this will help you in understanding the execution. Try yourself for fork() || fork() expression. Let me know if you have further questions.

H.S.
  • 11,654
  • 2
  • 15
  • 32
3

Short circuit evalutation

For the built-in logical AND operator, the result is true if both operands are true. Otherwise, the result is false. This operator is short-circuiting: if the first operand is false, the second operand is not evaluated

So for this line:

fork() && fork()

The first fork is evaluated true in the child and the child will fork one more (and the parent is done for this statement due to the short circuit).

The opposite happends with:

fork() || fork()

And the parent will fork one more.

Equivalent code

An equivalent way of writing the same is with an if statement:

if (fork()){
  fork();
}

and

if (!fork()){
  fork();
}
darune
  • 10,480
  • 2
  • 24
  • 62