While researching on file I/O in C, I came across two functions: fgetc()
and read()
.
//code1.c
#include <stdio.h>
int main(void)
{
char ch;
ch = fgetc(stdin);
return 0;
}
//code2.c
#include <unistd.h>
int main(void)
{
char ch;
read(STDIN_FILENO, &ch, 1);
return 0;
}
In both of the above programs, if I enter hello
:
The first one, will store the input from keyboard (stdin) in ch
and the program will just terminate. Meaning that ch
will contain h
and the remaining characters will just disappear.
In the second program, the input from keyboard will also be stored in ch
. But the remaining characters (ello
) will not disappear. Instead, they will be passed on to the terminal as a command after the program terminates.
I am not able to understand why is this happening? Is it something related to how inputs are buffered in C (and by computers in general)?
Yes, it is related to how inputs are buffered.
The standard I/O package functions for reading data (fgetc()
et al) will wait until data is made available by the terminal driver and will then read the available data from the terminal (usually a line full — with your example, the characters 'h'
, 'e'
, 'l'
, 'l'
, 'o'
, '\n'
) and the fgetc()
function will return the first, the 'h'
. Consequently, the other characters are not available to other programs.
The system call read()
will also wait for the terminal driver to make data available, but then reads only the first character, leaving the other characters available to other programs.
On POSIX-based systems, the fgetc()
function typically uses the read()
system call (indirectly) to get the data from the terminal, but it usually requests up to a buffer-full of data, which could be anywhere from 512 up to 8192 characters requested (or it could be bigger; it will usually be a power of two), but the read()
call will return with what's available. That's usually much less than a buffer-full when the input is a terminal. The rules are somewhat different when the input is a disk file, pipe or socket.
Note that the read()
system call does not add a null byte to the end of the data, so what it reads are not strings.
I've glossed over numerous details and caveats, seeking to keep my answer easy to understand while avoiding gross distortions of reality. There are ways to control the behaviour of terminals; I've described more or less what happens in the default case.