How does reading and writing to standard streams (stdin, stdout) work?
Let's begin with the notion that everything is a "file" in UNIX (should hold true for some other POSIX compliant systems)
This would mean that somewhere out there on your computer there's a "file" that corresponds to standard input and standard output and that you just need to read or write it.
How do we get access? We know that files are managed by the kernel. so we need some mechanism to ask the kernel to do this for us.
This type of request is called a system call or syscall for short.
Now that we know we need a syscall. What kinds of syscall are available? In C and C++ on UNIX systems the available syscalls are provided via a header file called unistd.h. It defines functions like open, close, fork, and pipe.
Two of those stick out immediately, open sounds like it could open a file, close looks like it could close a file once we were done with it. We also have read, and write. These read and write to a "file" as we'd expect.
with these four calls we could open a file, read from it, write to it, and close it when we are done. So we just need to know how to call these syscalls properly
Let's go over the prototypes for each of these functions
open
FILE *fopen(const char *restrict filename, const char *restrict mode);
FILE *fopen(const char *restrict filename, const char *restrict mode);
This tells us that open takes a string(char *) filename and another string mode and it returns FILE *.
the name of the function is fopen. and it returns a pointer to a FILE data structure.
the name of the function is fopen. and it returns a pointer to a FILE data structure.
fopen isn't in unistd.h. it's actally in stdio.h what it does is make the call to open() which is in unistd.h for you. I don't think the details of that are necessary here so we'll go to the read call next
read
int fscanf(FILE * __restrict, const char * __restrict, ...)
Now this makes sense. We can read from a file using fscanf which take a FILE * . Exactly what we got from fopen.
So we know that we can pass the pointer we got when opening the file to it to read from the file
int fscanf(FILE * __restrict, const char * __restrict, ...)
Now this makes sense. We can read from a file using fscanf which take a FILE * . Exactly what we got from fopen.
So we know that we can pass the pointer we got when opening the file to it to read from the file
again fscanf is one of a series of wrapper calls over the read() which is in unistd.h. At this point you know what I'm getting at. fwrite takes a FILE * and so does fclose. they write to a file and close the file respectively.
We know all of this involves dealing with FILE * but what does that pointer point to?
FILE is struct (built in C datatype) that holds information about a file you're working with. In particular it holds a field called a file descriptor. A file descriptor coincidentally is what those syscalls need to find your file.
So when you pass fclose a FILE pointer. it retrieves the file descriptor from FILE struct and passes it along to the syscall close() which can then close the file you're working with.
No we know how to wrangle files effectively but that doesn't tell us anything about stdin and stdout.
Follow me into stdio.h let's see what stdin and stdout really are.
They're FILE pointers.