1
\$\begingroup\$

This program in essence is extremely simple, however, it can be written in many different ways. I'm a beginner at C and I've written it in 5 different variations (mostly differing in the use of getchar over scanf, while over do while loops..).

Apart from readability and/or maintainability, is there any difference between these 5 code blocks (such as perhaps performance or efficiency, if we disregard the use of getchar over scanf)?

unsigned char d;
  1. while (1)
        while(scanf("%c", &d), d != '\n') {
            printf("%d\n", d * d);
        }  
    
  2. do {
        scanf(" %c", &d);
        printf("%d\n", d * d);
    } while (1);
    
  3. while (1)
        while((d = getchar()) != '\n') { //could also be written as while(d = getchar(), d != '\n')
            printf("%d\n", d * d);
        }
    
  4. while ((d = getchar())) {
        if(d == '\n')
            continue;
        printf("%d\n", d * d);
    }
    
  5. do {
        d = getchar();
        if(d == '\n')
            continue;
        printf("%d\n", d * d);
    } while (1);
    
\$\endgroup\$
4
\$\begingroup\$
while (1)
    while(scanf("%c", &d), d != '\n') {
        printf("%d\n", d * d);
    }

It's better to consistently add braces around the body of statements like while and if. A common mistake is to write something like this:

while (1)
    while(scanf("%c", &d), d != '\n') {
        printf("%d\n", d * d);
    }
    printf("...");

This may mislead a reader to think that the last printf is part of the outer loop, when it isn't. Just put the braces around the body always.

Another negative point in this version is having two statements inside the inner while. This is not easy to read. It's better to write in a way that there is no more than one statement per line. More generally, I recommend to avoid the , operator entirely.

The same point applies to this line in the 3rd version:

while((d = getchar()) != '\n') {

That is, there is an assignment within the conditional statement. Avoid such "clever" writing style. Stick to one statement per line.

And one more variation of the same issue, in the 4th version:

while ((d = getchar())) {
\$\endgroup\$
  • \$\begingroup\$ Can you elaborate on "most but not all skip \n" though? I've tested all of the examples, none of them catch \n and 100 is never printed. \$\endgroup\$ – bool3max Jul 7 '18 at 16:31
  • 2
    \$\begingroup\$ @BogdanM. Indeed you're right. I thought v2 does not skip \n like the others do, but in fact it does. It seems to be thanks to the space in the format string of scanf(" %c", &d);. This is confusing code that's best to avoid. \$\endgroup\$ – janos Jul 7 '18 at 16:55
  • 1
    \$\begingroup\$ I don't think any of them terminate when standard input is closed. \$\endgroup\$ – Toby Speight Jul 9 '18 at 9:59
  • 1
    \$\begingroup\$ @TobySpeight I mistakenly thought v4 does. But you're right, it doesn't thanks! \$\endgroup\$ – janos Jul 9 '18 at 10:39
2
\$\begingroup\$

There is a bug common to all 5 versions: we should always test whether the read was successful.

  • For scanf(): did it return 1, when we asked for one conversion?
  • For getchar(): did it return EOF?

    Note, in particular, that narrowing the result of getchar() (which is int) to an unsigned char loses the ability to distinguish EOF from a valid character.

Here's a corrected version:

#include <stdio.h>

int main(void)
{
    int d;
    while ((d = getchar()) != EOF) {
        if (d != '\n')
            printf("%d\n", d * d);
    }
}

Finally, a pedantic note: this will only work on ASCII values when run on an ASCII system/locale. In other words, C always uses the environment's character coding, and does not convert input to a fixed representation as done by, say, Java.

\$\endgroup\$
1
\$\begingroup\$

When it comes to performance, the only real way to know which method that is faster is to benchmark it. In many cases we can make educated guesses, but nothing can replace testing the code. Once snippet may win over another for various of reasons.

Apart from that, the biggest flaw is that you don't check for errors. getchar returns EOF on error. (Or of course end of file) scanf returns the number of successful assignments on success, and otherwise a negative value is returned.

\$\endgroup\$
  • \$\begingroup\$ @TobySpeight Yes it does. scanf("%c", &x) will read a character from stdin and write it to the variable x. The return value from scanf is the number of written characters, not the number of read characters. \$\endgroup\$ – klutt Jul 10 '18 at 8:24
  • \$\begingroup\$ @TobySpeight But yes, it is a bit ambiguous (and my post was slightly wrong) so I changed it. Thanks. \$\endgroup\$ – klutt Jul 10 '18 at 8:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.