3

I have compiled the following code with gcc

int main() {

  int a = 0;

  fprintf( stdin, "%d", 123 );
  fscanf( stdin, "%d", &a );
  printf( "%d\n", a );

  return 0;
}

In my expectation, the program should executes straightly (i.e., the program never pause and wait for user input). But it still stop, and wait for my input.

I want to know what happen when I try to write something to stdin and how to modify this code and it can execute straightly?

hwliu
  • 205
  • 2
  • 6
  • Related: http://stackoverflow.com/q/584868/694576 – alk Sep 13 '15 at 07:52
  • 1
    What are you trying to achieve? If there are circumstances where you want to use "123" instead of the input from stdin, you should write a function that simply returns "123" under those circumstances, or the value from stdin otherwise. Don't try to do it the hard way. – Mr Lister Sep 13 '15 at 08:15

5 Answers5

3

stdin is for input only, stdout is for output. (4566976's answer shows you what happens when you try to output to stdin) See for example the glibc documentation on standard streams

(in short, writing to stdin makes no sense at all)

3

If you print out the return value of fprintf(stdin you can see that the function call fails.

In the shell you can pipe something into the stdin of the process.

#include <stdio.h>

int main(void) {

  int a = 0, ret;

  printf("%d\n", ret = fprintf( stdin, "%d", 123 ));
  if (ret < 0) perror("fprintf");
  fscanf( stdin, "%d", &a );
  printf( "%d\n", a );

  return 0;
}

$ echo 123 | ./a.out
-1
fprintf: Bad file descriptor
123
$ 
4566976
  • 2,419
  • 1
  • 10
  • 14
3

In addition of the fprintf(stdin, bug you also forgot that stdin is not the keyboard. The latest C11 standard does not know about the keyboard. On a Linux graphical desktop, only the X11 server is reading from the physical keyboard.

Practically speaking, on POSIX systems notably such as Linux, stdin can be a pipe(7) (using pipelines in your shell is very common), a fifo(7), a socket(7), a plain file (thru redirection) or even /dev/null, and of course also a terminal.

The funny thing these days is that terminals are very often virtual emulated devices (I did not see any real physical terminal in this century, outside of museums), read about pseudotty. The details are quite arcane for historical reasons. Read the tty demystified page. See also ANSI escape code wikipage & console_codes(4) and tty(4) (so consider /dev/tty and perhaps /dev/console)

You can check (with isatty(3)) that stdin is a terminal (actually a pseudotty) using isatty(STDIN_FILENO)...

Practically speaking, when you really want to use the terminal, I strongly recommend using a library like ncurses or GNU readline (both are using termios(3))

Don't forget that I/O is generally buffered, and use fflush(3) wisely.

BTW, you should have compiled with all warnings & debug info (gcc -Wall -Wextra -g) then use the gdb debugger. And strace(1) would have been very useful too.

Maybe you wanted to pipe to your own program (but that is weird, and often wrong, unless you take great care about all the implications; it is however a very useful trick for handling signal(7) in event oriented programs, notably those with some GUI). Beware that pipes have a limited buffer size (so avoid deadlocks, probably by having your event loop with poll(2)) and read about PIPE_BUF and write. You might have tried:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
  int pfd[2] = {-1,-1};
  int a= 0;
  if (pipe(pfd)) { perror("pipe"); exit (EXIT_FAILURE); };
  if (dup2(pfd[0],STDIN_FILENO)<0)
      { perror("dup2 stdin"); exit(EXIT_FAILURE);};
  if (dup2(pfd[1],STDOUT_FILENO)<0) 
      { perror("dup2 stdout"); exit(EXIT_FAILURE);};
  if (printf("%d\n", 123)<=0) { perror("printf"); exit(EXIT_FAILURE); };
  if (fflush(stdout)) { perror("fflush"); exit(EXIT_FAILURE); };
  if (scanf("%d", &a)<1) { perror("scanf"); exit(EXIT_FAILURE); };
  if (a != 123) { fprintf(stderr, "impossible happened a=%d\n", a); };
  fprintf(stderr, "done...got a=%d\n", a);
}

You should read Advanced Linux Programming and learn more about syscalls(2); it has several chapters related to this. Read carefully pipe(2) and dup2(2) and be aware that the above program would be wrong for a larger output (bigger that PIPE_BUF, which on my system is several kilobytes)

BTW, you can get a readable FILE* from a memory buffer using fmemopen(3). For writing (e.g. with fprintf) to an output buffer, consider open_memstream and don't forget to fflush it before accessing the output buffer.

Community
  • 1
  • 1
Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • "Thanks a lot Basile!! I would try to study what you write in your answer. My current subject is binary translation and I believe that it will be helpful for me : )" – hwliu Sep 14 '15 at 12:39
2

You can ungetc() a few characters and then read them with fscanf().

#include <stdio.h>

int main()
{
    int value = 0;

    ungetc ( '\n', stdin);//reverse order. newline first here but last from fscanf 
    ungetc ( '3', stdin);
    ungetc ( '2', stdin);
    ungetc ( '1', stdin);
    fscanf ( stdin, "%d", &value);
    printf ( "value is %d\n", value);

    return 0;
}

output: value is 123

user3121023
  • 8,181
  • 5
  • 18
  • 16
1

You're simply incorrect thinking that fscanf(stdin, "format", ...); does not block and wait for input, because it does.

meaning-matters
  • 21,929
  • 10
  • 82
  • 142