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;
while (1) while(scanf("%c", &d), d != '\n') { printf("%d\n", d * d); }
do { scanf(" %c", &d); printf("%d\n", d * d); } while (1);
while (1) while((d = getchar()) != '\n') { //could also be written as while(d = getchar(), d != '\n') printf("%d\n", d * d); }
while ((d = getchar())) { if(d == '\n') continue; printf("%d\n", d * d); }
do { d = getchar(); if(d == '\n') continue; printf("%d\n", d * d); } while (1);
3 Answers 3
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())) {
-
\$\begingroup\$ Can you elaborate on "most but not all skip
\n
" though? I've tested all of the examples, none of them catch\n
and100
is never printed. \$\endgroup\$bool3max– bool3max2018年07月07日 16:31:58 +00:00Commented Jul 7, 2018 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 ofscanf(" %c", &d);
. This is confusing code that's best to avoid. \$\endgroup\$janos– janos2018年07月07日 16:55:24 +00:00Commented Jul 7, 2018 at 16:55 -
1\$\begingroup\$ I don't think any of them terminate when standard input is closed. \$\endgroup\$Toby Speight– Toby Speight2018年07月09日 09:59:58 +00:00Commented Jul 9, 2018 at 9:59
-
1\$\begingroup\$ @TobySpeight I mistakenly thought v4 does. But you're right, it doesn't thanks! \$\endgroup\$janos– janos2018年07月09日 10:39:50 +00:00Commented Jul 9, 2018 at 10:39
There is a bug common to all 5 versions: we should always test whether the read was successful.
For
scanf()
: did it return1
, when we asked for one conversion?For
getchar()
: did it returnEOF
?Note, in particular, that narrowing the result of
getchar()
(which isint
) to anunsigned char
loses the ability to distinguishEOF
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.
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.
-
\$\begingroup\$ @TobySpeight Yes it does.
scanf("%c", &x)
will read a character from stdin and write it to the variablex
. The return value from scanf is the number of written characters, not the number of read characters. \$\endgroup\$klutt– klutt2018年07月10日 08:24:15 +00:00Commented Jul 10, 2018 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– klutt2018年07月10日 08:30:21 +00:00Commented Jul 10, 2018 at 8:30