Can I access stdin without keyboard?

杀马特。学长 韩版系。学妹 提交于 2019-12-23 15:48:15

问题


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?


回答1:


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)




回答2:


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
$ 



回答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.




回答4:


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




回答5:


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



来源:https://stackoverflow.com/questions/32547384/can-i-access-stdin-without-keyboard

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!