Is getc() and getchar() well-defined function? - c

printf("%c %c", getc(stdin), getc(stdin));
rewind(stdin); printf("\n");
printf("%c %c", getchar(), getchar());
output
s
s
s
s
I wrote the source code like that and I pressed s, enter, s, enter sequentially on windows console application using vs2017. Console shows me what I put and print output when I press the key. The result's appearance is like s, enter, enter, space, s, s, enter, enter, space, s. Did I use the function in wrong way?
Q1. I pressed s, enter sequentially, but why output is reversed?
Q2. At output's third line and sixth line, why is there exist whitespace like  s not s?.

Explaining the output
The arguments to a function can be evaluated in any order that the compiler chooses.
The original code looked like this (missing printf("\n"); compared to the revised code):
printf("%c %c", getc(stdin), getc(stdin));
rewind(stdin);
printf("%c %c", getchar(), getchar());
Your 'output' in the question is extremely difficult to read and interpret. However, I'm reasonably sure that what you show consists of both your input and the program's output.
You say you type s enter twice. The output you get, copied from the question, is:
s
s
s
s
The first s and the newline that follows are what you typed. The blank line is there because the newline was read by the getc(stdin) that appears first in the argument list, then there's the blank from the format string, and the s which was read by the getc(stdin) that appears second in the argument list. Your compiler, it seems, evaluates the arguments right to left — but you should not rely on that.
Now we run into a problem; there's a newline after the second s that isn't accounted for by the code you showed when I wrote this answer. Since then, you've added the printf("\n"); which explains the output you saw.
Putting that aside, the third s is followed by a newline and is what you typed. And again, the blank line is from the newline and then space and s is the rest of the output, and there's probably a newline after that too.
JFTR, when I run your code (program getc73 compiled from getc73.c), I see:
$ getc73
s
ss
s$
$
The $ is my prompt; the first s is what I type; then the newline, blank and s are printed (with no newline at the end); then the second s on the third line is my next s and return, followed by the newline and blank s. Since there's no newline at the end, my prompt appears immediately after the fourth s. I hit return to print another prompt at the start of the line.
Why rewind(stdin) is a no-op
You can't successfully execute an fseek() call on a terminal device. They don't have any storage so you can't move around like that. The rewind() function is effectively:
void rewind(FILE *fp)
{
fseek(fp, 0, SEEK_SET);
}
So, the rewind(stdin) call fails to rewind, but it has no way to report that to you because it returns no value. It does no good; it doesn't do any harm either. If it had worked, you would not have needed to enter the information twice.
If the standard input had been a file instead of a terminal, then the rewind would succeed — disks are seekable.
Improved formatting
If you were on a POSIX system, I'd suggest you used a feature of POSIX printf() that allows you to print the same argument more than once:
#include <stdio.h>
int main(void)
{
printf("[%1$c] %1$d [%2$c] %2$d\n", getc(stdin), getc(stdin));
printf("[%1$c] %1$d [%2$c] %2$d\n", getchar(), getchar());
return 0;
}
When I run that on my terminal (it's a Mac running macOS Mojave 10.14.6 using GCC 9.2.0), I get:
s
[
] 10 [s] 115
s
[
] 10 [s] 115
The lines with just the s on them are what I typed; the other lines show that the newline is printed before the letter s.
This probably won't work for you in VS2017 — the manual for printf() formats on Windows strongly suggests it won't work. Your best bet, probably, is to use:
#include <stdio.h>
int main(void)
{
printf("%d %d\n", getc(stdin), getc(stdin));
printf("%d %d\n", getchar(), getchar());
return 0;
}
That generates:
s
10 115
s
10 115
Choosing a good format to show what you're seeing is an art form. Don't forget to include newlines at the end of (most) printf() format strings. Obviously, if you're building up a line of output piecemeal, you don't want newlines at the end of intermediate formats, but the majority of printf() statements should have a newline at the end of the format.

Related

I am not able to execute a code in C | Some sort of error that doesn't show up [duplicate]

The following code produces a very strange result when I run it.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
for ( ; ; )
{
char test;
printf("Please enter 'w' ");
scanf("%c", &test);
printf("%c\n", test);
if (test == 'w')
{
printf("Working\n");
}
else
{
printf("ERROR\n");
return 0;
}
}
}
What I want to happen is for the whenever I input 'w' it continues the loop so I can input 'w' again. What it does though is go to the else statement even though I input 'w'. It just seems to skip the scanf() line. I have asked everyone I know who knows C but they do not know how to solve it.
Somebody please help me out here!
This is because you type w followed by ENTER. So there are 2 characters in the input, 'w', followed by a newline (\n). The latter causes the else branch to be taken on the second iteration.
Note that standard input is line buffered when connected to a terminal. If you need to deal with characters immediately, there are ways to do that. See the comp.lang.c FAQ for details ("How can I read a single character from the keyboard without waiting for the RETURN key? How can I stop characters from being echoed on the screen as they're typed?").
Note that for robust programming it is a must to check the return value of scanf. It returns the number of successfully converted items. As shown, your code does not handle the case of end-of-file properly, i.e. when the user types Ctrl-D (assuming Unix terminal). Then scanf returns EOF and no conversion was performed, but you use test as if it contained a meaningful value.
as Jens said. you have to ignore the newline '\n'
Adding a space at the beginning of the format specifier " %c" will ignore the newline '\n'
scanf(" %c", &test);
Using " %c" will also ignore other white spaces like \t space \b \v \r
As Jens says, you must consume '\n', use getchar() after scanf()
You need to do something like
scanf("%c", &test);
while(getchar()!='\n');
scanf takes input upto space or \n (whichever comes first) and leaves the \n in the buffer

scanf character input/output

scanf not working as expected with a normal input character
So I was just trying out the scanf() and a normal input/output of this function. I know that I had to leave a space before the input character and the operand% so my code is as below. Somehow I don't understand why whatever the input I inserted the output remains 0.
#include<stdio.h>
int main()
{
char c ;
scanf(" %c",&c);
printf("%c",c);
return 0;
}
I was expecting the output will be whatever the character I insert. For example, insert an "A" via keyboard and the output will be exactly an "A".
I'm using a vim environment to edit my code, but I found that if I run this code on codeblocks it works. What's the difference?
It appears you are not providing any non-whitespace (which are eaten away by th space in the format string, including newlines) characters (or perhaps nothing at all) from standard input. If this happens, scanf will fail to parse a char and leaves c uninitialized.
Using uninitialized variable is is Undefined Beheavior, so in theory anything could happen. In practice, from your description, it sounds like memory reserved for c happens to have byte value 0, which is unprintable character, so printf prints something else (maybe /0, maybe nothing). And then the environment (vim) might show you the program exit code, also 0 here (assuming the Undefined Behavior doesn' cause your program to crash).
To fix this, check return value of scanf:
#include<stdio.h>
int main()
{
char c ;
int r = scanf(" %c",&c);
if (r==1) {
// Always print something and add newline
// to be sure we see some output always.
printf("c='%c'\n",c);
} else {
printf("scanf error: %d\n", r);
// If r==-1, errno variable tells what error was
}
return 0;
}
Practical hint: To provide standard input when there is no terminal (so you can't type the input), you can pipe something:
echo A | ./thisprogram
I've tried to run your program (exactly as it appears in your question)
pru.c
#include<stdio.h>
int main()
{
char c ;
scanf(" %c",&c);
printf("%c",c);
return 0;
}
and with the input you posted in your question (just A plus Enter) and got the following result:
$ cc pru.c
$ ./a.out
A
A$ _
which is exactly the expected output. So the problem must be in another place, or you have a completely different scenario and need to provide more information.
I tested this on a PC (Intel Core Duo) with FreeBSD 12.0/CLANG compiler. (here $ is the unix prompt and _ is the cursor after the run) And of course, the program has been edited with vi(1) (this has no impact on the result).
Edit
Try to change
printf("%c",c);
by this
printf("0x%02x, [%c]\n",c,c);
so, you'll get an hex dump of the character just input, and also its representation as printed. The \n at the end is to ensure your shell prompt is not eating (overwriting) the last line output of your program (mostly if you have changed the prompt variable PS1) hidding the printed char.
That should produce (on your posted input) the following output:
$ cc pru.c <--- compilation of new pru.c
$ ./a.out <--- default name for your program executable.
A <--- this is your input (followed by <return>)
0x41, [A] <--- this should be your program output.
$ _ <--- prompt (and cursor) after your program execution

Treatment of \b in template strings to printf in C

Can someone please explain or point me to a reference that explains why in the code snippets below the line printed in the first includes the whole word hello, while in the second it consists only of the letters he? I thought the backspace escape characters would delete the last three letters regardless of the newline escape. I've read the GNU documentation on printf but couldn't find anything on point.
int main(void)
{
printf("hello\b\b\b\n");
return 0;
}
int main(void)
{
printf("hello\b\b\b");
return 0;
}
This isn't really a question about C or printf. In each case the code does exactly what it says: in the first example it outputs the 9 characters h e l l o \b \b \b \n and in the second it likewise outputs 8 characters. If you write standard output to a file that's exactly what the file will contain. But if you write to a terminal, your terminal may handle \b by backspacing, and so your question is really about your terminal. You have not said what terminal you are using.
One common way for the backspace character to behave is by moving the cursor one position left, but not actually erasing the character in that position. The next character output will be written over it.
In your first example, you move the cursor back to the first l, but then you never write anything over it or any other character. Then \n doesn't change any character on the screen, but just moves the cursor to the next line. So you still see hello on the preceding line.
In your second example, the cursor is left over the first l when your program terminates. So the next program to write output to the terminal will write over that character (unless the next program's first write is \n or something like that). That next program might be your shell, and so the characters from your shell prompt probably overwrite the llo, so you don't see them.
Two key things to keep in mind:
Output to the console isn't immediately rendered when you use printf(): the output is buffered. Two ways to immediately (synchronously) flush the output buffer to the console is to print a newline via printf("\n"), or manually flush the buffer via fflush(stdout).
If you use the \b escape sequence, you will move the cursor one "space" back/left, but printing a newline character won't necessarily clear the rest of the line. If you were to manually print new characters over the old ones (i.e. printf("Hello\b\b\b \n")) the output would be more like what you're expecting.
Additionally, if you want to do some advanced command-line menus, graphics, etc, you'll need to use something like libncurses.
Finally, the following example should help give a better idea on the typically behavior involving your question.
#include <stdio.h>
#include <unistd.h>
#define DELAY (2) /* seconds */
int main(void)
{
printf("Example DELAY:\n");
printf("------------------------------------------------------------------------\n");
printf("hello\b\b\b\n");
sleep(DELAY);
printf("\n\n");
printf("Example 2:\n");
printf("------------------------------------------------------------------------\n");
printf("hello\b\b\b");
sleep(DELAY);
printf("...");
sleep(DELAY);
fflush(stdout);
sleep(DELAY);
printf("\n\n");
printf("Example 3:\n");
printf("------------------------------------------------------------------------\n");
fflush(stdout);
printf("hello\b\b\b");
fflush(stdout);
sleep(DELAY);
printf("...");
sleep(DELAY);
fflush(stdout);
sleep(DELAY);
printf("\n\n");
return 0;
}

C skips user input from subsequent scanf statement [duplicate]

I've been having a lot of problems trying to figure out how to use scanf(). It seems to work fine with integers, being fairly straight forward scanf("%d", &i).
Where I am running into issues is using scanf() in loops trying to read input. For example:
do {
printf("counter: %d: ", counter);
scanf("%c %c%d", &command, &prefix, &input);
} while (command != 'q');
When I enter in a validly structured input like c P101, it seems to loop again before prompting me. This seems to happen even with a single:
scanf("%c", &c)
in a while loop. It'll do the loop twice before prompting me again. What is making it loop twice, and how do I stop it?
When I enter in less amount of input that programmatically wouldn't have another character or number such as q, pressing enter seems to prompt me to enter more. How do I get scanf() to process both single and double character entries?
When you enter "c P101" the program actually receives "c P101\n". Most of the conversion specifiers skip leading whitespace including newlines but %c does not. The first time around everything up til the "\n" is read, the second time around the "\n" is read into command, "c" is read into prefix, and "P" is left which is not a number so the conversion fails and "P101\n" is left on the stream. The next time "P" is stored into command, "1" is stored into prefix, and 1 (from the remaining "01") is stored into input with the "\n" still on the stream for next time. You can fix this issue by putting a space at the beginning of the format string which will skip any leading whitespace including newlines.
A similiar thing is happening for the second case, when you enter "q", "q\n" is entered into the stream, the first time around the "q" is read, the second time the "\n" is read, only on the third call is the second "q" read, you can avoid the problem again by adding a space character at the beginning of the format string.
A better way to do this would be to use something like fgets() to process a line at a time and then use sscanf() to do the parsing.
It's really broken! I didn't know it
#include <stdio.h>
int main(void)
{
int counter = 1;
char command, prefix;
int input;
do
{
printf("counter: %d: ", counter);
scanf("%c %c%d", &command, &prefix, &input);
printf("---%c %c%d---\n", command, prefix, input);
counter++;
} while (command != 'q');
}
counter: 1: a b1
---a b1---
counter: 2: c d2
---
c1---
counter: 3: e f3
---d 21---
counter: 4: ---e f3---
counter: 5: g h4
---
g3---
The output seems to fit with Robert's answer.
Once you have the string that contains the line. i.e. "C P101", you can use the parsing abilities of sscanf.
See:
http://www.cplusplus.com/reference/clibrary/cstdio/sscanf.html
For question 1, I suspect that you've got a problem with your printf(), since there is no terminating "\n".
The default behavior of printf is to buffer output until it has a complete line. That is unless you explicitly change the buffering on stdout.
For question 2, you've just hit one of the biggest problems with scanf(). Unless your input exactly matches the scan string that you've specified, your results are going to be nothing like what you expect.
If you've got an option you'll have better results (and fewer security issues) by ignoring scanf() and doing your own parsing. For example, use fgets() to read an entire line into a string, and then process the individual fields of the string — maybe even using sscanf().
Perhaps using a while loop, not a do...while loop will help. This way the condition is tested before execution of the code.
Try the following code snippet:
while(command != 'q')
{
//statements
}
Also, if you know the string length ahead of time, 'for' loops can be much easier to work with than 'while' loops. There are also some trickier ways to dynamically determine the length as well.
As a final rant: scanf() does not "suck." It does what it does and that is all.
The gets() function is very dangerous (though convenient for no-risk applications), since it does not natively do any checking of the input. It is VERY commonly known as a point of exploit, specifically buffer overflow attacks, overwriting space in registers not allocated for that variable. Therefore if you choose to use it, spend some time putting some solid error checking/correction in.
However, almost invariably, either fgets() or POSIX getline() should be used to read the line — noting that the functions both include the newline in the input string, unlike gets(). You can remove the trailing newline from string read by either fgets() or getline() using string[strcspn(string, "\n")] = '\0'; — this works reliably.

Parsing input with scanf in C

I've been having a lot of problems trying to figure out how to use scanf(). It seems to work fine with integers, being fairly straight forward scanf("%d", &i).
Where I am running into issues is using scanf() in loops trying to read input. For example:
do {
printf("counter: %d: ", counter);
scanf("%c %c%d", &command, &prefix, &input);
} while (command != 'q');
When I enter in a validly structured input like c P101, it seems to loop again before prompting me. This seems to happen even with a single:
scanf("%c", &c)
in a while loop. It'll do the loop twice before prompting me again. What is making it loop twice, and how do I stop it?
When I enter in less amount of input that programmatically wouldn't have another character or number such as q, pressing enter seems to prompt me to enter more. How do I get scanf() to process both single and double character entries?
When you enter "c P101" the program actually receives "c P101\n". Most of the conversion specifiers skip leading whitespace including newlines but %c does not. The first time around everything up til the "\n" is read, the second time around the "\n" is read into command, "c" is read into prefix, and "P" is left which is not a number so the conversion fails and "P101\n" is left on the stream. The next time "P" is stored into command, "1" is stored into prefix, and 1 (from the remaining "01") is stored into input with the "\n" still on the stream for next time. You can fix this issue by putting a space at the beginning of the format string which will skip any leading whitespace including newlines.
A similiar thing is happening for the second case, when you enter "q", "q\n" is entered into the stream, the first time around the "q" is read, the second time the "\n" is read, only on the third call is the second "q" read, you can avoid the problem again by adding a space character at the beginning of the format string.
A better way to do this would be to use something like fgets() to process a line at a time and then use sscanf() to do the parsing.
It's really broken! I didn't know it
#include <stdio.h>
int main(void)
{
int counter = 1;
char command, prefix;
int input;
do
{
printf("counter: %d: ", counter);
scanf("%c %c%d", &command, &prefix, &input);
printf("---%c %c%d---\n", command, prefix, input);
counter++;
} while (command != 'q');
}
counter: 1: a b1
---a b1---
counter: 2: c d2
---
c1---
counter: 3: e f3
---d 21---
counter: 4: ---e f3---
counter: 5: g h4
---
g3---
The output seems to fit with Robert's answer.
Once you have the string that contains the line. i.e. "C P101", you can use the parsing abilities of sscanf.
See:
http://www.cplusplus.com/reference/clibrary/cstdio/sscanf.html
For question 1, I suspect that you've got a problem with your printf(), since there is no terminating "\n".
The default behavior of printf is to buffer output until it has a complete line. That is unless you explicitly change the buffering on stdout.
For question 2, you've just hit one of the biggest problems with scanf(). Unless your input exactly matches the scan string that you've specified, your results are going to be nothing like what you expect.
If you've got an option you'll have better results (and fewer security issues) by ignoring scanf() and doing your own parsing. For example, use fgets() to read an entire line into a string, and then process the individual fields of the string — maybe even using sscanf().
Perhaps using a while loop, not a do...while loop will help. This way the condition is tested before execution of the code.
Try the following code snippet:
while(command != 'q')
{
//statements
}
Also, if you know the string length ahead of time, 'for' loops can be much easier to work with than 'while' loops. There are also some trickier ways to dynamically determine the length as well.
As a final rant: scanf() does not "suck." It does what it does and that is all.
The gets() function is very dangerous (though convenient for no-risk applications), since it does not natively do any checking of the input. It is VERY commonly known as a point of exploit, specifically buffer overflow attacks, overwriting space in registers not allocated for that variable. Therefore if you choose to use it, spend some time putting some solid error checking/correction in.
However, almost invariably, either fgets() or POSIX getline() should be used to read the line — noting that the functions both include the newline in the input string, unlike gets(). You can remove the trailing newline from string read by either fgets() or getline() using string[strcspn(string, "\n")] = '\0'; — this works reliably.

Resources