Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: fopen() only sets errno in POSIX

Many functions require the allocation of multiple resources. Failing and returning somewhere in the middle of this function without freeing all of the allocated resources could produce a memory leak. It is a common error to forget to free one (or all) of the resources in this manner, so a goto chain is the simplest and cleanest way to organize exits while preserving the order of freed resources.

Noncompliant Code Example (POSIX)

In this noncompliant example, exit code is written for every instance in which the function can terminate prematurely. Notice how failing to close fin2 produces a resource leak, leaving an open file descriptor.

Please note that these examples assume errno_t and NOERR to be defined, as recommended in DCL09-C. Declare functions that return errno with a return type of errno_t. An equivalent compatible example would define errno_t as an int and NOERR as zero.

These examples also assume that errno is set if fopen() fails. This is guaranteed by POSIX but not by C11. See ERR30-C. Set errno to zero before calling a library function known to set errno, and check errno only after the function returns a value indicating failure for more details.

Code Block
bgColor#FFCCCC
langc
typedef struct object {  /* Generic struct: contents don't matter */
  int propertyA, propertyB, propertyC;
} object_t;

errno_t do_something(void){
  FILE *fin1, *fin2;
  object_t *obj;
  errno_t ret_val;
  
  fin1 = fopen("some_file", "r");
  if (fin1 == NULL) {
    return errno;
  }

  fin2 = fopen("some_other_file", "r");
  if (fin2 == NULL) {
    fclose(fin1);
    return errno;
  }

  obj = malloc(sizeof(object_t));
  if (obj == NULL) {
    ret_val = errno;
    fclose(fin1);
    return ret_val;  /* Forgot to close fin2!! */
  }

  /* ... More code ... */

  fclose(fin1);
  fclose(fin2);
  free(obj);
  return NOERR;
}

This is just a small example; in much larger examples, errors like this are even harder to detect.

Compliant Solution (POSIX)

In this revised version, a goto chain replaces each individual return segment. If no error occurs, control flow falls through to the SUCCESS label, releases all of the resources, and returns NOERR. If an error occurs, the return value is set to errno, control flow jumps to the proper failure label, and the appropriate resources are released before returning.

...