The C Programming Language says that I/O is not built into C and is instead accessed through stdio.h. But if stdio.h is written in C, how does stdio.h implement I/O?
I've looked at the source code and I don't understand it.
When K&R write that I/O isn't "built into" C, they mostly mean that I/O statements are not part of the language grammar.  By contrast, Fortran 77 has distinct I/O statements, such as the PRINT statement:
PRINT *, 'The value of X is', X
The C stdio library functions and the Fortran I/O statements all delegate I/O operations to the underlying system.  It's just that in C's case they bypass having to parse and translate high-level I/O statements into machine code.  
The standard library is typically going to call something in the OS to handle reading and writing the actual file. On POSIX (or similar) that would mostly be read and write, which can read or write files. On Windows, it'd typically be ReadFile and WriteFile. Windows also supplies ReadFileEx and WriteFileEx, but they're mostly for doing asynchronous I/O, which the standard library doesn't (yet) support.
From there, execution will typically descend through (at least) a file system driver, a file system cache, and a device driver for the disk, SSD, or whatever sort of device is being used to store the file in question. Alternatively, the data could go through a sockets layer, and from there to a device driver for a network interface.
The precise details of transferring the data to the hardware varies (pretty widely), but in a typical case on modern hardware a great deal of it is actually handled by the target device itself. On the CPU you create a gather list for output or a scatter list for input. This is basically just a small chunk of the CPU's page tables, to tell target device what physical pages it needs to read (for output) or write (for input).
This is necessary because (at least on most typical OSes) you're allocating memory with virtual addresses--what your program sees as "contiguous" memory may be scattered around rather randomly in physical memory. Most hardware1 doesn't know about that address translation, however, so it needs to be supplied with the physical addresses of the individual pages of memory where you data resides.
Also note that in a typical case (e.g., a typical file on disk), what you issue as a single call to fwrite might get translated into a number of actual I/O operations with the disk drive. If you write part of a sector, your write might translate to reading an entire sector into a buffer, overwriting part of that buffer, then writing the modified buffer out to the disk.
1. The major exceptions are some video adapters, and RDMA network adapters. These have memory in which the device driver maintains a shadow copy of the CPU's address translation tables, which the hardware then uses to translate the addresses, thus reducing the load on the CPU.
I/O operations are not part of the C language in the sense that the names and signatures of the I/O functions are not magically know in your program. Instead you need a #include <stdio.h> to tell the compiler about the names and signatures of the I/O functions.
The implementation of those functions is in the standard library, which may or may not be written in C, although for the I/O functions it is likely that they are written in C and delegate some work to functions provided by the operating system.
Device drivers are very hardware dependent. Some hardware uses memory mapped I/O (that is you read/write to dedicated memory addresses to control I/O devices). Others have special purpose registers and opcodes. While the first could be handled with C, it still varies from machine to machine (timing, addresses). The second simply needs assembler to talk to the hardware properly. Still you can use C in this case, but it contains a lot of direct assembler code. So actually
accessed through
stdio.h
means that behind this header you will find very low level stuff.
There are quite some answer but none talk about File Descriptor / Handle, which is the core about how IO are managed by the OS.
When you open a file, or a socket, or anything else you use for IO, you get a pointer to a FIle Descriptor/FD (linux) or an Handle in Windows which is the same.
Basically the OS, whether it's Linux or Windows, has a table of those FD/Handle and when you call an IO on them, you're not writing in the file, you're asking to the OS that you want to perform the operation X on the handle Y. Those things are so low-level (see @JerryCoffin answer) that you don't do that yourself, instead you use stdio library which wrap this for you. The rest it's the OS, with all the drivers and hardware that will handle the stuff for you.
This is thanks to that abstraction (FD/Handles+OS+drivers) that you can use File operations quite transparently whatever the Disk is Formatted in FAT/NTFS/EXT4. Though there are some limits (ie : FAT 32 : no more than 4G file).
stdio.cimplements "standard I/O," which is what you quoted is referring to. Standard I/O is very simple to use. Under the hood, very intricate machine code (which is what C compiles to) accesses ports, moves bytes, and in general does all of the low-level things that make "standard I/O" possible. That low-level stuff is much more difficult to write yourself, but it can be done, and the result would be a library file similar tostdio.c.<stdio.h>is not the source code of the implementation of standard IO, but mostly just of declarations of functions and types used in it. So you'll better say thatstdio.his declaring I/O functions, not implementing them. On my system standard I/O is implemented in somelibc.soshared library and in the Linux kernel.