oadams
oadams

Reputation: 3087

stdout redirect changing output

I have a program called abc.

When I run the following command:

$ ./abc < infile

I get the following output:

ijklm

However, when I run:

$ ./abc < infile > outfile
$ cat outfile

I am given this output:

ijkoo

Now, I'm assuming this is a bug with my program. However, regardless of what my program is doing, I do not know how this is possible.

EDIT:

Now that I know it is possible, I'm curious as to what it is in my program that is causing this.

There is a block inside a loop in my program that contains:

byte = ascii_to_byte(asciibyte);
putchar(byte);

byte is of type char.

Now if I change putchar(byte) to printf("%c", byte) all the output stays the same.

However, if I change it to printf("%d", byte), then $ ./abc < infile outputs:

105106107111111

Which is the decimal representation of those ascii characters as they were in outfile. But it's not the decimal representation of the characters as they actually appeared when they just got sent to stdout. I don't understand why there could be this difference.

EDIT #2:

If I change the printing line to printf("%c\n", byte), then $ ./abc < infile outputs:

i
j
k
o
o

This is consistent with what goes into outfile. Again, not sure what the difference is.

EDIT #3

I just tested this on a 32 bit machine, and the program works: outputfile contains ijklm. Wierd.

EDIT #4

Here is the main function:

int main()
{
    char asciibyte[8];
    char byte;

    int c; //Using int to avoid the EOF pitfall.
    long charcount = 0;

    while((c = getchar()) != EOF){
        if(c != '0' && c != '1'){
            continue;
        }
        asciibyte[charcount % 8] = c;
        if(charcount % 8 == 7){
            /*Testing revealed that at this point asciibyte does contain
            what it should contain, eight ASCII ones and zeros representing
            a byte read in from stdin*/
            byte = ascii_to_byte(asciibyte);
            /*Print statements such as:
                printf("%d", byte);
                printf("%c\n", byte);
            reveal that the ascii_to_byte function works incorrectly on my
            64 bit machine. However these statements:
                putchar(byte);
                printf("%c", byte);
            make it appear as though the function operates as it should.
            EXCEPT if i redirect that output to a file.*/
            putchar(byte);
        }
        charcount++;
    }
    return 0;
}

And here is the ascii_to_byte function:

char ascii_to_byte(char *asciibyte){
    char byte;
    int i;
    for(i = 0; i < 8; ++i){
        if(asciibyte[7-i] == '1'){
            byte = byte | (1 << i);
        }
    }
    return byte;
}

FINAL EDIT

I noticed that I should have initialized byte to 0x00. Problem solved. Why am I this retarded? I'll give answer points to whoever can explain specifically how this caused the problem.

Upvotes: 6

Views: 747

Answers (4)

pmg
pmg

Reputation: 108968

As you say, byte is uninitialized, so anything may happen.

One of the things that may happen, is that byte "starts" at 0 and keeps its value from function call to function call (as if it was declared static).

in binary ...

   byte    |    c (bin)   |   byte | c
-----------+--------------+--------------
 00000000  | i (01101001) | 01101001 (i)
 01101001  | j (01101010) | 01101011 (k) * strange you get 'j', but anything can happen :)
 01101011  | k (01101011) | 01101011 (k)
 01101011  | l (01101100) | 01101111 (o)
 01101111  | m (01101101) | 01101111 (o)

Upvotes: 1

Matthew Slattery
Matthew Slattery

Reputation: 46998

This sort of weird behaviour, which comes and goes according to seemingly unrelated changes, is probably indicative of your program reading from or writing to memory it shouldn't, and the behaviour changing as other parts of the code make different use of the stack and/or heap.

I would check your code carefully for badness such as buffer overruns, functions which return pointers to variables on the stack, etc.

Stepping through your code with a debugger may well be productive (or it could change the behaviour again if you're unlucky!).

There are a couple of interesting things you've seen:

  1. How can the redirection of stdout possibly affect anything? Perhaps because it causes the C library to behave a bit differently: a different buffering mode is used for the stream, depending on whether it is connected to a terminal device or not (see the GNU libc documentation, or C99 §7.9.13 para. 7).

  2. Why does changing putchar(byte) to printf("%c", byte) not change anything, when both printf("%d", byte) and printf("%c\n", byte) do change the behaviour? Perhaps because the compiler automatically rewrites printf("%c", byte) to the more efficient putchar(byte) - vaguely recent versions of GCC normally do this, even when no optimisations are enabled - whereas printf("%d", byte) and printf("%c\n", byte) really will be compiled as calls to printf().

Upvotes: 3

Daren Thomas
Daren Thomas

Reputation: 70314

What Neil Butterworth says. The function is called isatty.

if (isatty(STDOUT)) printf("I am printing to the terminal!\n");

Also, while testing stuff, you might have done:

$ ./abc < infile > infile

by accident. So, you might want to check quickly, that infile really contains the same data.

Upvotes: 0

user2100815
user2100815

Reputation:

It's certainly possible - the program could check if it is writing to a terminal and write something different from what it would write when writing to a pipe.

Upvotes: 1

Related Questions