putchar() weird output, why is this happening?
Asked Answered
Z

4

10

If I type the words "Hello World" into the standard input stream, this program will print out weird box symbols instead of the expected "Hello World" back into standard output.

#include <stdio.h>

int main(void)
{
    // print out all characters from the stream until '/n' character is found
    int ch;
    while (ch = getchar() != '\n')
    {
        putchar(ch);
    }
    putchar('\n');
}

I am aware of how to fix the problem. But why is this line of code incorrect?

while (ch = getchar() != '\n')
Zoila answered 27/3, 2015 at 7:12 Comment(6)
You can find a list of operator precedences here by the way: en.cppreference.com/w/c/language/operator_precedence. (Felt a bit silly posting an answer with the million that just came in. :D)Multivocal
Interesting, I did not think that the operator precedence in C would differ from C# and Java.Zoila
If you're thinking of this particular case, then it seems to be the same in Java and C#. It would be bad if x_eq_y = x == y was interpreted as (x_eq_y = x) == y, as a silly example. C has some wonky precedences though (which have been acknowledged by the authors as being a mistake, but are still emulated by other languages for compatibility). For example, x == y << z is the same as x == (y << z) as you'd expect, while x == y & z is the same as (x == y) & z.Multivocal
Try this in Java: (fooBoolean = fooInteger == barInteger). The == operator has higher precedence than the = operator.Zoila
@JohnH In any case, operator precedence in Java and C# differ from C's. Not the other way round ('cause C is older). Additionally in order to never fail again in precedence in C, you must remember that multiplication and division have higher precedence than addition and subtraction. Everything else needs parenthesis.Ciapas
IMO this is exactly why the "single statement -> single responsibility -> single action" principle was created... so that people won't have a WTF operator precedence every 5 minutes. Either separate compound statements or use explicit parentheses. In any other case you're asking for trouble or forcing it upon other people.Denver
B
30

(ch = getchar() != '\n') should be rewritten as

((ch = getchar()) != '\n')

Because != binds tighter than = in C operator precedence table. Operator are not ordered from left to right (reading direction of english) as one might expect. For example result of 2 + 3 * 5 is 17 and not 25. This is because * will be performed before performing +, because * operator has more precedence than + operator.

So when you write something like

ch = getchar() != '\n'

You expect it to be equivalent to: (ch = getchar()) != '\n'

But actually it is equivalent to: ch = (getchar() != '\n')

Because the result of != is either true or false, you see character \001 on screen. I believe \001 appears as boxes1 on your system.


1: Character \001 may appear as a box or dot or some wierd character or it may not appear in output at all.

Bronez answered 27/3, 2015 at 7:15 Comment(3)
In reference to For example result of 2 + 3 * 5 is 17 and not 25....This is a great answer but, Had it been 2 * 3 + 5, it would still be evaluated as 11 and not as 16.. It has got to do with the * operator precedence over + than Right to Left evaluation. Some rookie would take this wrongly.\Telpher
@Telpher Thanks for your helpful comment, I will update my answer accordingly.Bronez
By the program logic, \000 will be seen "never", not "rarely". As soon as gethcar() returns '\n', ch is assigned 0 and the loob body is not executed.Idolum
C
12

And as a slightly meta-ish answer, the overarching fix is always compiling with warnings enabled:

$ gcc t.c -Wall
t.c: In function ‘main’:
t.c:7:5: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
     while (ch = getchar() != '\n')
     ^
t.c:12:1: warning: control reaches end of non-void function [-Wreturn-type]
 }
 ^

Or better yet try clang, which warns by default and generally gives better diagnostic messages:

$ clang t.c
t.c:7:15: warning: using the result of an assignment as a condition without parentheses [-Wparentheses]
    while (ch = getchar() != '\n')
           ~~~^~~~~~~~~~~~~~~~~~~
t.c:7:15: note: place parentheses around the assignment to silence this warning
    while (ch = getchar() != '\n')
          ^
           (                     )
t.c:7:15: note: use '==' to turn this assignment into an equality comparison
    while (ch = getchar() != '\n')
              ^
              ==
1 warning generated.
Cumshaw answered 27/3, 2015 at 11:34 Comment(0)
N
8

You need to be aware of operator precedence - comparison operators such as != have a higher precedence than assignment (=). Use parentheses to enforce the required behaviour, i.e. change:

while (ch = getchar() != '\n')

to:

while ((ch = getchar()) != '\n')


Addendum: be sure to take heed of the advice from @TomGoodfellow in a separate answer below - using a decent compiler with warnings enabled (e.g. gcc -Wall) would have alerted you to this problem immediately.
Northerner answered 27/3, 2015 at 7:15 Comment(0)
F
5

Because you need to write it as while ((ch = getchar()) != '\n')

Fomalhaut answered 27/3, 2015 at 7:15 Comment(1)
While you're very right, you should add some description about what, how and why in your answer to make it a great one. Cheers !! :-)Vespasian

© 2022 - 2024 — McMap. All rights reserved.