As far as I understood one end of a pipe has both read and write fd's and the  other end also has read and write fd's. Thats why when we are writing  using fd[1], we are closing the read end e.g. fd[0] of the same side  of the pipe and  when we are reading from the 2nd end using fd[0] we close the fd[1] of that end. Am I correct? 
Does one end of a pipe have both read and write fd?
pipe;ipc
Yes, a pipe made with pipe() has two file descriptors. fd[0] for reading and fd[1] for writing. No, you do not have to close either end of the pipe, it can be used for bidirectional communication.Edit: in the comments you want to know how this relates to ls | less, so I'll explain that too:Your shell has three open file descriptors: 0 (stdin), 1 (stdout) and 2 (stderr). When a shell executes a command it does something like this (I've simplified it a bit:pid = fork();if(pid == 0) {    /* I am the child */    execve(...); /* Whatever the user asked for */}else {    waitpid(pid); /* Wait for child to complete */}File descriptors 0, 1 and 2 are inherited by the child, so input/output works as expected. If you do ls | pipe, something slightly different happens to do the redirection:int pipe[2];pipe(pipe, 0);pid1 = fork();if(pid1 == 0) {    /* This is ls, we need to remap stdout to the pipe. We don't care about reading from the pipe */    close(pipe[0]);    close(1);    dup2(pipe[1], 1);    execve(...);}else {    pid2 = fork();    if(pid2 == 0) {        /* This is less, it reads from the pipe */        close(pipe[1]);        close(0);        dup2(pipe[0], 0);        execve(...);    }    else {        waitpid(pid1);        waitpid(pid2);    }}So the shell creates the pipe, forks, and just before executing it remaps the pipe to the stdin or stdout of the child processes, making data flow from process one to process 2. As shell pipes are not bidirectional, they only use one end of the pipe and close the other end (it actually closes the other end too, after duplicating the filedescriptor to stdin or stdout).