There are lot's of articles that introduces way of using pipe and redirecting standard IO with those.
But, whenever try to do this, there is always big issue - buffer mode!
See following example.

< Tested on [Ubuntu EGLIBC 2.12.1-0ubuntu9] + [Ubuntu 2.6.35-23-generic-pae] >

#include <stdio.h>
#define _tstr "Sample Text\n"
int
main () {
    int fdp[2];
    pipe (fdp);
    if (0 != fork ()) {/* parent */
        dup2 (fdp[1],1); /* redirect standard out */
        /* fdp is not used anymore */
        close (fdp[0]);
        close (fdp[1]);
        [*A] /* <--- see below */
        sleep(99999999);
    } else {
        int  rb;
        char buf[100];
        dup2 (fdp[0],0); /* redirect standard in */
        /* fdp is not used anymore */
        close (fdp[0]);
        close (fdp[1]);
        if (0 >= (rb = [*B])) perror("IO Error\n"); /* <-- see below for [*B] */
        buf[rb] = 0; /* add trailing 0 */
        printf ("read from input:%s\n", buf);
        sleep(99999999);
    }
}

< *** [*A][*B] pair and result. *** >
OK pairs
    [*A] : write (fdp[1], ...)   |   [*B] : read (fdp[0], ...)
    [*A] : write (1, ...)        |   [*B] : read (fdp[0], ...)
    [*A] : write (1, ...)        |   [*B] : read (0, ...)
    [*A] : printf (_tstr); fflush (stdout) | [B] : read (1, ...)

NOT OK pairs - printed output is "read from input:" ('_tstr' is not printed immediately)
    [*A] : printf (_tstr);       | [*B] : read (0, ...)
        -> 'fflush' is missing here. But '\n' is at the end of test string...

Why 'printf' doesn't work without 'flush'?
'printf' uses standard buffer (at first IO operation, buffer is allocated by using 'malloc').
And because, output device is pipe - not console, buffered mode is used.
So, until flushing, all outputs are stored in buffer (NOT device).
To make pipe be REALLY like standard IO, mode of those buffer should be LINE BUFFERED mode.
So, 'setvbuf() or setlinebuf()' should be used at the first of [*A] as follows.

[*A] : setlinebuf (stdout); printf (_tstr);
    OR setvbuf (stdout, (char*)NULL, _IOLBF, 0); printf (_tstr);

It is simple, isn't it?

Here is more complicated cases.
See following example.

< Tested on [Ubuntu EGLIBC 2.12.1-0ubuntu9] + [Ubuntu 2.6.35-23-generic-pae] >

< main.c >
#include <stdio.h>
int
main() {
    int fdp[2]; /* pipe */
    pipe (fdp);
    if (0 != fork()) { /* parent */
        dup2 (fdp[1], 1); /* redirect standard out */
        close (fdp[0]);
        close (fdp[1]); 
        [*C] /* <-- see below */
        execlp ("test", (char*)0); /* run test (*1) */
    } else {
        int  rb;
        char buf[100];
        dup2 (fdp[0],0); /* redirect standard in */
        /* fdp is not used anymore */
        close (fdp[0]);
        close (fdp[1]);
        if (0 >= (rb = [*B])) perror("IO Error\n");
        buf[rb] = 0; /* add trailing 0 */
        printf ("read from input:%s\n", buf);
        sleep(99999999);
    }

< test.c > => test (executable)
int
main () {
    printf("This is Test!\n");
    sleep(99999999);
}

As above case, string from 'test' - "This is Test!" - is not printed to console immediately because it is buffered.
(Assume that, < test.c > SHOULD NOT be modified!)
Is there solution? Yes.
Before moving next step, see this post first.

Combination of LD_PRELOAD and  __attribute__ ((constructor)) is solution.
To do this, new file is added to make share library that will be preloaded.

< mystdbuf.c > => libmystdbuf.so
#include <stdio.h>
__attribute__ ((constructor)) static void
mystdbuf () {
    setvbuf (stdout, (char*)NULL, _IOLBF, 0);
}

And add following codes to section [*C]

putenv ("LD_PRELOAD=./libmystdbuf.so");

Resolved!

Another easy and popular solution is using 'stdbuf' command in gnu core-utils.
Replace (*1) with

execlp ("stdbuf", "stdbuf", "-oL", "./test", (char*)0);

As described in LINK above, mechanism of 'stdbuf' is exactly same with above manual solution!
Done!

What is stdout, stdin and stderr?
Actually, I am using these without deep understanding.
Now, it's time to deep-dive to it.

Let me focus on stdout. (other twos are similar.)
What type of file default stdout is? Is it normal file? No, definitely. It's device file.
So, two different processes can write data to same stdout directly.
(Normal file doesn't allow this! )
I can easily know which device is used as stdout by checking devices.
(By entering following command in console.

ls -al /dev | grep stdout
    -> stdout -> /proc/self/fd/1

Interesting isn't it?
Let me move forward.

ls -al /proc/self/fd/1
    -> 1 -> /dev/pts/7

Right!.
Even if every process access standard output device with same name 'stdout', it branches to appropriate devices by symbolic link - '/proc/self' is used.
Now I can guess what redirecting stdout is. Let me check it.

# redirecting stdout with sample program.
# something like this.
# main() { for(int i=0; i<10000000; i++) { sleep(1); printf("+"); fflush(stdout); }}
$ ./a.out > t &
$ ls -al /proc/<child pid>/fd/1
    -> 1 -> /xxxx/xxx.../t <= path of target file 't'

Done!

+ Recent posts