It comes down to the fact that reads from disks are block oriented: to read a single byte from a disk, the hardware ends up reading a block (512 or 1024 or some number) of bytes, buffering all that, passing it to the kernel. If you read byte 0 of the file from block 0 of the file, do some work, then read byte 1 from the file, the kernel probably ends up reading in block 0 of the file again. And again for byte 2, and again for byte 3. Yes, there's potential caching, both in the kernel and in the disk drive itself, but the kernel handles a lot of processes, so maybe not.
read() call also has to change the CPU from user state to kernel state. Memory mappings change, at the very least. Probably a lot of other not so obvious things happen. That can take time, too.
read() system call changes CPU state, and could entail disk I/O.
getc() can buffer an entire disk block (or more) in user space, so maybe 512 calls to
getc() causes the kernel to read in a single disk block, with a single state change. If you look in
stdio.h you will find a macro for a constant
BUFSIZ which is supposed to be an efficient (disk-block-multiple) size for a
read() or a
write() system call to a file on disk.