A failure is a detected circumstance in which an operation cannot proceed normally or has not produced a valid result. Reliable C programs check preconditions, validate library-call outcomes, and handle detected errors instead of ignoring them. section 15.6 section 15.5
Checking a library result
if (puts("hello world") == EOF) { perror("can't output to terminal:"); exit(EXIT_FAILURE); } The comparison uses the function's documented failure return. perror then reports a diagnostic using errno, and exit terminates the program. section 8.1.3
Handle failures according to their source and available indicators:
- Prevent wrongdoing by checking operation preconditions and choosing an alternative when they are not met. section 15.5
- Check operations that may exhaust resources, such as allocation, for error returns; preserve and restore errno when cleanup requires it. section 15.5 section 15.6
- Interpret failure indicators according to each C library function: null pointers, special codes, nonzero values, or negative values may have different meanings. section 8.1.3
- Organize complicated recovery and cleanup with function-specific error paths, including goto-based cleanup blocks where appropriate. section 15.7
Listing 15.1 Printing an array of numbers
int fprintnumbers(FILE*restrict stream,
char const form[restrict static 1],
char const sep[restrict static 1],
size_t len, size_t nums[restrict len]) {
if (!stream) return -EFAULT;
if (len && !nums) return -EFAULT;
if (len > INT_MAX) return -EOVERFLOW;
size_t tot = (len ? len : 1)*strlen(sep);
int err = errno;
char* buf = nullptr;
if (len) {
/* Count the chars for the numbers. */
for (size_t i = 0; i < len; ++i)
tot += snprintf(nullptr, 0, form, nums[i]);
/* We return int so we have to constrain the max size. */
if (tot > INT_MAX) return error_cleanup(EOVERFLOW, err);
}
buf = malloc(tot+1);
if (!buf) return error_cleanup(ENOMEM, err);
sprintnumbers(tot, buf, form, sep, len, nums);
/* print whole line in one go */
if (fputs(buf, stream) == EOF) tot = EOF;
free(buf);
return tot;
}
Use the annotated implementation to trace precondition checks, size-overflow detection, allocation failure, errno preservation, and output failure in one C function. section 15.6
Not every failure is recoverable. Signals, traps, termination functions, and thread termination provide different interruption or cleanup behavior, while Byzantine failure may continue degrading system state without a capturable error. section 15.5











