C Basics: Distinguish a Short Binary Read from an Error
A short read is not automatically an I/O error. Read a small binary stream into a larger buffer and check why fewer bytes arrived.
Prepare a scratch directory
Tested on Linux amd64 with GCC 14.2.0. Install GCC and make gcc available on PATH. You also need Bash, a text editor, and the standard mktemp, rm, and rmdir utilities. This example uses the C standard library.
Keep the same terminal open. Stop if setup fails, before saving the source:
start_directory="$PWD"
work_directory="$(mktemp -d)" || exit 1
cd -- "$work_directory" || exit 1Run the example
Save this as fread-stream-status-demo.c:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
const unsigned char original[] = {0x41, 0x00, 0xff};
unsigned char received[8];
FILE *stream = tmpfile();
if (stream == NULL) {
perror("tmpfile");
return EXIT_FAILURE;
}
if (fwrite(original, 1, sizeof original, stream) != sizeof original ||
fseek(stream, 0, SEEK_SET) != 0) {
perror("prepare stream");
fclose(stream);
return EXIT_FAILURE;
}
size_t received_count = fread(received, 1, sizeof received, stream);
printf("Bytes read: %zu\n", received_count);
for (size_t index = 0; index < received_count; ++index) {
printf("%02X%s", (unsigned int)received[index],
index + 1 == received_count ? "\n" : " ");
}
int failed = ferror(stream) != 0;
printf("EOF: %d; error: %d\n", feof(stream) != 0, failed);
if (fclose(stream) != 0) {
perror("fclose");
return EXIT_FAILURE;
}
return failed ? EXIT_FAILURE : EXIT_SUCCESS;
}Compile and run it. The second command runs only after compilation succeeds:
gcc -std=c17 -Wall -Wextra -Werror -pedantic fread-stream-status-demo.c -o fread-stream-status-demo &&
./fread-stream-status-demoObserved output:
Bytes read: 3
41 00 FF
EOF: 1; error: 0Read the result
Using an element size of one makes the return value a byte count. Only those bytes are printed; the unused part of the buffer is never read. The request exceeds the three-byte file, so the EOF indicator is set while the error indicator stays clear. The zero byte is ordinary binary data.
Apply it carefully
Check the read result first, then stream status. Do not use a pre-read EOF test as the loop condition. An exact-size read can succeed without setting EOF until a later read attempts to pass the end. This example demonstrates the EOF path, not an injected read failure. The temporary stream is removed when closed.
The official reference documents the behavior used here.
Clean up
Remove only the source and executable created for this example, then return to the starting directory:
rm -- fread-stream-status-demo.c fread-stream-status-demo
cd -- "$start_directory" || exit 1
rmdir -- "$work_directory"
unset start_directory work_directoryIf the directory is not empty, inspect its remaining contents before removing anything else.
Acknowledgements
This post was created with assistance from AI.