3

I am new to C and come from a back ground of newer languages (like Java and C++) and I'm not sure how to handle runtime errors like an incorrect arguments sent to a function.

for example say I want to write a function to manipulate a string (assuming any valid int is an acceptable return value):

int foo (char s[]) {
    if ( strlen(s) < 1)
        // ERROR
    ....
    ....
    return someInt;
}

How do I handle this scenario in Ansi C if I want the function to stop immediately? In C++ or Java I would throw an exception to be caught by the caller.

m.s.
  • 16,063
  • 7
  • 53
  • 88
dranobob
  • 796
  • 1
  • 5
  • 19
  • typically you display an error message, and then do `exit(error_code)` – Michał Szydłowski Aug 19 '15 at 13:54
  • There are many schools of thought on how to handle errors like these, often clashing with each other. The most common is to return silently (if the function returns `void`) or to return with a code or value meaning error (typically `-1` or `NULL`), possibly setting `errno`. However, one thing that most people seem to agree on is to *not* use `assert` or similar functionality for runtime error checking, mainly because it's only enabled on debug builds. – Some programmer dude Aug 19 '15 at 13:56
  • related: http://stackoverflow.com/questions/385975/error-handling-in-c-code – ryanpattison Aug 19 '15 at 13:56

5 Answers5

3

You can always use the return value :

if( /* some bad parameters */ )
   return -1;

And then :

int value = foo( something );
if( value == -1 )
   // error
else
   // no error

Or pass an another parameters :

int foo( char s[], int* value )
{
    if( /* error */ )
        return 1;// error code 1
    // ...
    *value = something;
    return 0;
}

Then when you call the function you can verify if it was executed correctly :

 if( foo( "something", &result ) )
 {
     //ok
 }
 else
 {
     // not ok
 }

Both of these method implies that the caller will verify manually if there was an error

pascx64
  • 904
  • 16
  • 31
2

if I want the function to stop immediately

Two things.

  1. If you want only the function to stop execution and return to caller, use a return statement. Also, you may want to use some predefined or user-defined error codes as the failure case return value to distinguish the reason of failure from the caller.

  2. If you want the program itself to terminate, call exit().

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
  • Let's assume my goal is to try and recover. Since the function returns an int, but any int is valid, is there a way to return, but let the caller know the function failed. – dranobob Aug 19 '15 at 13:57
  • @dranobob you can use a particular style to return values, like example, a `0` return value means success, and a negative return value means the function call was unsuccessful. Now, based on the -ve return value, you can check the cause of failure. – Sourav Ghosh Aug 19 '15 at 13:59
  • @dranobob FWIW, you can check the concept of [`errno`](http://linux.die.net/man/3/errno) – Sourav Ghosh Aug 19 '15 at 14:00
  • 1
    My advice would be to avoid `errno` and other static variables. They make your code messy, especially if multithreading. – M.M Aug 19 '15 at 14:04
  • @MattMcNabb errno is thread-local AFAIK – Diego Aug 19 '15 at 14:05
  • @MattMcNabb Sir, I was just showing the concept of error codes, I was also not advising to use errno. :) – Sourav Ghosh Aug 19 '15 at 14:05
  • ok, so it appears my logical fallacy is using the return int as the result of the function. Instead it sounds like I should use the return int as an error code. Then pass a reference as a parameter and have the function set the reference's value as my result. – dranobob Aug 19 '15 at 14:08
  • @dranobob That'll be the best approach. It will make your function much more extensive and robust. – Sourav Ghosh Aug 19 '15 at 14:09
  • @dranobob That is the best way indeed. You'll notice that experienced C programmers sometimes refrain from returning values from functions even when they could have done so, just because that they want to reserve the return for error handling. – Lundin Aug 19 '15 at 14:10
  • @Lundin Ahead of you sir by +/- 20 seconds. :) – Sourav Ghosh Aug 19 '15 at 14:11
  • Its a complete mental shift than what I'm used to, but it makes complete sense! Thanks everyone for the help. – dranobob Aug 19 '15 at 14:12
1

The standard way to do error handling in any C program is to reserve the return value of the function for an error code.

Most typically this is a custom enum type. Example:

typedef enum
{
  FOO_OK,
  FOO_ERR_STRLENGTH,
  FOO_ERR_DIVIDE_BY_ZERO,
  ...
} foo_err_t;


foo_err_t  foo_func (/* parameters */)
{
  if (strlen(s) < 1) 
  {
    return FOO_ERR_STRLENGTH;
  }

  ...

  return FOO_OK;
}

Then you document the function properly and state which error codes it may return, and what is causing them.

What should be done upon error is usually no business of your routines, but something that should be decided by the caller. In particular, your routines should never decide to terminate the whole program. That decision should be taken by the outer-most caller (top of the call stack), i.e from main().

Lundin
  • 195,001
  • 40
  • 254
  • 396
0

If I thinking of your question right, most type errors will be caught on compilation. If you want to just return early, you can simply call return. Often times, error handling, C programers will add a goto for the error:

struct Data *
create_data ()
{
  struct Data *data = malloc(sizeof(struct Data));

  if (data == NULL)
      goto exit;

  /* do stuff with data */

exit:
    return data;
}

That will return NULL if there was no memory. But you might want to exit early for another reason. So, if you need to exit the program gracefully (and free all the things you have taken), you're going to need a central exit point.

I usually have a function like

void bail (const char *fmt, ...)
{
    va_list argp;
    va_start(argp, fmt);
      vfprintf(stderr, fmt, argp);
    va_end(argp);
    exit(EXIT_FAILURE);
}

It prints to STDERR and returns a non-0 exit as well. You could easily modify this to accept any data you need to free.

Leroy
  • 237
  • 2
  • 12
  • While this is one of the few valid uses of goto, I don't agree that goto error is the ideal way of doing this. You'll want to specify the error reason somewhere anyhow, so it is better just to return the error code, without any jumping around. – Lundin Aug 19 '15 at 14:06
  • I feel that it depends on whether you want the NULL malloc error to float up (to let some other system handle it) or whether your're creating the system that handles the error. – Leroy Aug 19 '15 at 14:08
  • Most often you'll want all errors to "float up", especially when designing libraries, drivers etc. – Lundin Aug 19 '15 at 14:12
0

Your major options are:

  • Have the return value combine both the error status and the actual value. For example if your function only intends to return non-negative values, then you could say that negative return values indicate an error.
  • Return an error code (0 for success) and give the function's output via a reference parameter.
  • Return the function's output and give the error code via a reference parameter.

Other options include using a global (or thread-local) variable for error status (not a good idea IMHO), or returning a struct that contains both the return information and the error status.

M.M
  • 138,810
  • 21
  • 208
  • 365