Robert C. Seacord reminds us in “Effective C” that puts
can fail:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
puts("Hello, world!");
return EXIT_SUCCESS;
}
Although it’s unlikely that the puts function will fail and return EOF for our simple program, it’s possible. Because the call to puts can fail and return EOF, it means that your first C program has a bug.
If we want to be strict about it, he suggests we check for EOF
(a macro defined as a negative integer):
#include <stdio.h>
#include <stdlib.h>
int main(void) {
if (puts("Hello, world!") == EOF) {
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
But how could puts
(or indeed printf
) fail? Can we force a write error to see it ourselves?
Redirect output to a full or nearly full disk
Let’s make a 1MB disk image, mount it and check available space:
> hdiutil create -size 1M -fs HFS+ -volname SmallDisk ~/Desktop/SmallDisk.dmg
> hdiutil attach ~/Desktop/SmallDisk.dmg
> df -h /Volumes/SmallDisk
Filesystem Size Used Avail Capacity iused ifree %iused Mounted on
/dev/disk4s1 984Ki 144Ki 840Ki 15% 5 4.3G 0% /Volumes/SmallDisk
840 Kibibytes (KiB) or 860,160 bytes are left on our disk after overhead.
We’ll amend our C hello world to output something larger than that, say, a million “a” characters:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
char *str = malloc(1000001);
if (str == NULL) {
fprintf(stderr, "Memory allocation failed\n");
return EXIT_FAILURE;
}
memset(str, 'a', 1000000);
str[1000000] = '\0';
if (puts(str) == EOF) {
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Then redirect output from stdout to our little disk at /Volumes/SmallDisk
:
> ./hello > /Volumes/SmallDisk/file.txt
No output. Check the exit code:
> echo $?
1
We made puts
fail! What about our file contents?
> cat /Volumes/SmallDisk/file.txt
>
Empty! puts
returned EOF
before buffer contents flushed to disk. (With printf
, we might expect file.txt
to include some of our “a“s but not all of them.)
Why does this matter?
It usually doesn’t. We often skip the check and accept the risk of failure.
It matters more when you’re depending on the exit code. The same code above without the EOF
check will always return a 0 exit code, even when puts
fails to write due to disk, permissions, or hardware issues.