...
This noncompliant code example consists of two application-independent functions f()
and g()
. The f()
function is part of the external API for the module; the g()
function is an internal function.
Code Block | ||||
---|---|---|---|---|
| ||||
void g(void) { /* ... */ if (something_really_bad_happens) { fprintf(stderr, "Something really bad happened!\n"); abort(); } /* ... */ } void f(void) { g(); /* ... do the rest of f ... */ } |
...
One way to indicate errors is to return a value indicating success or failure. This compliant solution ensures each function returns a value of type errno_t
, where 0 indicates that no error has occurred.
Code Block | ||||
---|---|---|---|---|
| ||||
const errno_t ESOMETHINGREALLYBAD = 1; errno_t g(void) { /* ... */ if (something_really_bad_happens) { return ESOMETHINGREALLYBAD; } /* ... */ return 0; } errno_t f(void) { errno_t status = g(); if (status != 0) return status; /* ... do the rest of f ... */ return 0; } |
...
Instead of encoding status indicators in the return value, each function can take a pointer as an argument, which is used to indicate errors. In the following example, each function uses a errno_t\ *
argument to report errors.
Code Block | ||||
---|---|---|---|---|
| ||||
const errno_t ESOMETHINGREALLYBAD = 1; void g(errno_t * err) { if (err == NULL) { /* Handle null pointer */ } /* ... */ if (something_really_bad_happens) { *err = ESOMETHINGREALLYBAD; } else { /* ... */ *err = 0; } } void f(errno_t * err) { if (err == NULL) { /* Handle null pointer */ } g(err); if (*err == 0) { /* ... do the rest of f ... */ } return 0; } |
...
The original errno
variable was the Standard C library's implementation of error handling using this approach.
Code Block | ||||
---|---|---|---|---|
| ||||
errno_t my_errno; /* also declared in a .h file */ const errno_t ESOMETHINGREALLYBAD = 1; void g(void) { /* ... */ if (something_really_bad_happens) { my_errno = ESOMETHINGREALLYBAD; return; } /* ... */ } void f(void) { my_errno = 0; g(); if (my_errno != 0) { return; } /* ... do the rest of f ... */ } |
...
The following example uses setjmp()
and longjmp()
to ensure that control flow is disrupted in the event of error, and also uses the my_errno
indicator from the previous example. See recommendation MSC22-C. Use the setjmp(), longjmp() facility securely for more info on setjmp()
and longjmp()
.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <setjmp.h> const errno_t ESOMETHINGREALLYBAD = 1; jmp_buf exception_env; void g(void) { /* ... */ if (something_really_bad_happens) { longjmp(exception_env, ESOMETHINGREALLYBAD); } /* ... */ } void f(void) { g(); /* ... do the rest of f ... */ } /* ... */ if (setjmp(exception_env) != 0) { /* if we get here, an error occurred; do not continue processing */ } /* ... */ f(); /* if we get here, no errors occurred */ /* ... */ |
...