The short answer is that you are creating multiple processes, which run asynchronously with respect to each other. The long answer follows:
When you type ./a.out at the shell prompt, that creates a process running your program. Let's call that process 1.
Process 1 calls fork(). This creates a new child process, Process 2, and both 1 and 2 carry on execution after the first fork() call, proceeding to the second fork() call. Process 1 creates child Process 3, and Process 2 creates child process 4. All four processes carry on from after the second fork(), proceeding to the final fork() call. Process 1 creates Process 5; Process 2 creates Process 6; Process 3 creates Process 7; and Process 4 creates Process 8.
Note that these process numbers are arbitrary: there's no guarantee they'd be created in that order.
The asynchrony comes into play as soon as that first fork() gets executed. The system offers no guarantees about scheduling the parent with respect to the child. Theoretically the child could run to completion before the parent continues, the parent could finish before the child gets any resources. The most likely scenario lies somewhere in the middle: the original process shares resources with its progeny, so that all run concurrently.
The final piece of the puzzle results from the fact that the shell is waiting for Process 1 to complete.Only Process 1. The shell doesn't know (or care) that Process 1 has started other processes. So when Process 1 completes, the shell displays a prompt. As it happened, some of the descendants of Process 1 hadn't yet reached the printf() statement. By the time they got there, the shell had already displayed its prompt.
To explore this further, you might want to try changing the fork() calls toprintf( "%d\n", fork() ); and/or changeprintf("Hello World\n") toprintf("Hello from pid %d\n", getpid() )