Q:

When is a process in UNIX terminated?

I am confused about the ending of a process in UNIX. When is a process in UNIX terminated? Should we necessarily write exit(0)  to terminate a process? In my view- no, but I cannot figure out the difference between those two pieces of code.
pid_t pid=fork();
if(pid < 0)
{
             perror("Fork error\n");
             return 1;  
}
else if (pid==0) /* child */
{
             //Do some operations here
}
else
{
            wait(NULL);
            //Do some operations
}

and
pid_t pid=fork();
if(pid < 0)
{
                 perror("Fork error\n");
                 return 1;  
 }
 else if (pid==0) /* child */
 {
                 //Do some operations here
                 exit(0);
 }
 else
 {
                wait(NULL);
                //Do some operations here
 }

Probably we need the exit(0) only for the wait() function. In this case, what would happen if we make wait() without having done exit(0)
 before? 

A:

POSIX
See exit():

Terminating a Process
It is important that the consequences of process termination as described occur regardless of whether the process called _exit() (perhaps indirectly through exit()) or instead was terminated due to a signal or for some other reason.
[...]
As required by the ISO C standard, using return from main() has the same behavior (other than with respect to language scope issues) as calling exit() with the returned value. Reaching the end of the main() function has the same behavior as calling exit(0).

See also wait(). Normal termination:

If wait() or waitpid() return because the status of a child process is available, these functions shall return a value equal to the process ID of the child process. [...] The value stored at the location pointed to by stat_loc shall be 0 if and only if the status returned is from a terminated child process that terminated by one of the following means:

The process returned 0 from main().
The process called _exit() or exit() with a status argument of 0.
The process was terminated because the last thread in the process terminated.

List of specified termination reasons:

WIFEXITED(stat_val)
Evaluates to a non-zero value if status was returned for a child process that terminated normally.
WIFSIGNALED(stat_val)
Evaluates to a non-zero value if status was returned for a child process that terminated due to the receipt of a signal that was not caught (see <signal.h>).

and respective status information:

WEXITSTATUS(stat_val)
If the value of WIFEXITED(stat_val) is non-zero, this macro evaluates to the low-order 8 bits of the status argument that the child process passed to _exit() or exit(), or the value the child process returned from main().
WTERMSIG(stat_val)
If the value of WIFSIGNALED(stat_val) is non-zero, this macro evaluates to the number of the signal that caused the termination of the child process.

Summary
To sum up, POSIX mentions two ways how a process can be terminated:

process can terminate itself, if it calls exit(), _exit() or returns from main() or last thread of the process terminates;
process can be killed by signal (sent by kernel, or some another process, or the process itself).

Threads
See also "Is it OK to call pthread_exit from main?" if you need to prevent all threads to terminate when main() returns.

Using exit() in child

In this case, what would happen if we make wait() without having done exit(0) before?

fork() duplicates current process and both parent and child continue execution from the same point when fork() returns.
Consider the following example:
int main() {
    if (fork() == 0) { /* child */
        foo();
    }
    else { /* parent */
        wait(NULL);
    }

    bar(); /* called in parent in child */
    return 0; /* or exit(0) */
}

In this example, child does not call exit() in its if branch, so after calling foo() it calls bar() and than returns from main() causing child process termination. In other words, both child and parent call bar() here.
Usually it's not what you want and you write this instead:
int main() {
    if (fork() == 0) { /* child */
        foo();
        exit(0);
    }
    else { /* parent */
        wait(NULL);
    }

    bar(); /* called only in parent */
    return 0;
}

In this case, child exits immediately after calling foo() so that bar() in not called in child.

