liveBook · Manning

Failure

As explained in Modern C, Third Edition

An error condition is a detected circumstance indicating that an operation cannot proceed normally or has not produced a valid result.

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

How Modern C, Third Edition covers it

How deep it goes
Develops precondition checks, return-value conventions, errno preservation, cleanup paths, and limits of recovery; defers detailed signal and threading treatment.
Examples
Uses puts with EOF, perror and exit, plus fprintnumbers with invalid arguments, overflow, allocation failure, errno restoration, and output errors.
What it assumes
Assumes familiarity with C functions and library calls while teaching failure indicators, precondition validation, errno, and cleanup strategy.
What you can do after
Diagnose and handle C failures by checking documented results, inspecting errno, selecting recovery paths, and centralizing cleanup.
How it connects
Connects failure with preconditions, error returns, errno, resource exhaustion, cleanup, signals, traps, and program termination.

Also explained in

Compare how 12 books explain failure →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Modern C, Third Edition. The section links point to the parts of the book it draws on.