C Using Goto Error Handling
Contents |
crystal-clear structure, without giving much thought to the often ugly topic of error handling. But unfortunately in programming, perhaps more than in any other kind of engineering, the devil is in the details. The handling on error goto handler of errors and of irregular inputs and data usually requires more code than the straight-line
On Error Goto Catch
algorithm for solving the problem itself. This is a regrettable but unavoidable artifact of our craft. But wait, there's more. As difficult as
Arrow Antipattern
error handling is, coupled with resource allocation and the need for robust deallocation it is nothing short of a huge headache. Fortunately, in newer high-level languages this is less of a problem because of automatic garbage collection. Also,
C Error Handling Best Practices
C++ provides tolerably robust solutions in the form of RAII. But as the title states, here I'm concerned with C, which doesn't have exceptions and destructors, so the issue is much more difficult. In this article I will argue that the much hated goto statement is a valuable tool for simplifying error-handling code in C. A simple case Here's a quote from the Wikipedia article on RAII: C requires significant administrative code since it doesn't support exception handling in c language exceptions, try-finally blocks, or RAII at all. A typical approach is to separate releasing of resources at the end of the function and jump there with gotos in the case of error. This way the cleanup code need not be duplicated. The code sample the article shows is this: int c_example() { int ret = 0; // return value 0 is success FILE *f = fopen("logfile.txt", "w+"); if (!f) return -1; if (fputs("hello logfile!", f) == EOF) { ret = -2; goto out; } // continue using the file resource // ... // Releasing resources (in reverse order) out: if (fclose(f) == EOF) ret = -3; return ret; } Sure, by inverting the logical comparison, this can be rewritten without a goto as follows: int c_example() { int ret = 0; // return value 0 is success FILE *f = fopen("logfile.txt", "w+"); if (!f) return -1; if (fputs("hello logfile!", f) != EOF) { // continue using the file resource } else { ret = -2; } if (fclose(f) == EOF) ret = -3; return ret; } Although we've gotten rid of the goto, IMHO this code isn't much cleaner. Note that we've just moved the mainline code into a condition. Will we do it for any error condition the function encounters? A thornier case Now consider this snippet: int foo(int bar) { int return_value = 0; allocate_res
program comes out of an error condition gracefully. On encountering an error a program must rollback and free-up various resources allocated during the course of execution. Ideally, the program should get back to a state where it was consistent when to use goto c# before the error happened. Why error handling is important? Absence of error handling or goto considered harmful a buggy error handling can lead a program or the system as whole to an inconsistent state. Often many fatal programming bugs such as c goto memory leaks, dead locks, data race conditions etc. are the results of an improper error handling. Especially in a programming environment where resources are scarce and margin of error is thin adapting to a good error handling technique becomes http://eli.thegreenplace.net/2009/04/27/using-goto-for-error-handling-in-c even more important. Common error handling techniques There are mainly two techniques used by professional C programmers to handle errors. The first one is where a program on encountering an error un-dos the changes and returns from the same code location. As a sample following is a C function that inserts some string in a linked list node if the string is already not present. It returns pointer to head of the linked list in case of success http://blog.staila.com/?p=114 or NULL otherwise. Notice the error handling technique used in the program. struct lnode { char *str; struct lnode *next; }; struct lnode *insert(char *data, int len, struct lnode *list) { struct lnode *p, *q; p = (struct lnode *)malloc(sizeof(struct lnode)); if ( NULL == p ) { return NULL; } p->str = (char *)malloc(sizeof(char)*len); if ( NULL == p->str ) { // free node before returning. free ( p ); return NULL; } memcpy ( p->str, data, len ); if(NULL == list) { p->next = NULL; list = p; } else { q = list; while(q->next != NULL) { // check for duplicates if (0 == strcmp(q->str,p->str)) { // free string and node free(p->str); free(p); return NULL; } q = q->next; } p->next = q->next; q->next = p; } return list; } In this case error is handled by freeing up memory allocated so far and returning from the same place. It is a very simple way of handling error gracefully but it has several demerits. It makes the function look complicated as it has multiple exit points (return statements). In this approach if the error handling part of the code grows large then the code can become really unmanageable. Thus such a technique of error handling can be used, at the most, only in functions which are quite small in size. The second type of error handling technique, which i
on October 24, 2007 | 1 Comment Not all use of goto is considered evil by C programmers. Used appropriately, it can make code easier to read, and increase code performance. It is often you have a series of mallocs. https://silviocesare.wordpress.com/2007/10/24/a-good-use-of-goto-for-error-handling/
static char *a, *b, *c, *d;
void do_initialization(void)
{
http://www.cprogramming.com/tutorial/goto.html a=malloc(10);
b=malloc(10);
c=malloc(10);
d=malloc(10);
}
To be robus, each malloc should be checked for failure. The do_initialization should reflect either success or failure. The following code implements that, but introduces more bugs.
int do_initialization()
{
if ((a = malloc(10)) == NULL) return -1;
if ((b = malloc(10)) error handling == NULL) return -1;
if ((c = malloc(10)) == NULL) return -1;
if ((d = malloc(10)) == NULL) return -1;
return 0;
}
Memory leaks have been introduced. Lets fix that.
int do_initializae()
{
if ((a = malloc(10)) == NULL) return -1;
if ((b = malloc(10)) == NULL) {
free(a);
return -1;
}
if ((c = malloc(10)) == on error goto NULL) {
free(b);
free(a);
return -1;
}
if ((d = malloc(10)) == NULL) {
free(c);
free(b);
free(a);
}
return 0;
}
That correctly implements do_initialization, but it appears somewhat redundant and can be hard to read in larger projects. A good use of goto can make the above code much better.
int do_initialization()
{
if ((a = malloc(10)) == NULL) return -1;
if ((b = malloc(10)) == NULL) goto err_a;
if ((c = malloc(10)) == NULL) goto err_b;
if ((d = malloc(10)) == NULL) goto err_c;
return 0;err_c: free(c);
err_b: free(b);
err_a: free(a);
return -1;
}The code demonstrated above is very often seen in production code. The opensource kernels and other large C projects, make extensive use of this type of construct.
Like this:Like Loading... Related This entry was posted in C Programming. Bookmark the permalink. ← Linux Kernel *fpos += count signed integer overflowbug Kernel Pointer Overflows in Read andWrites → One response to “A good use of goto for errorhandling” Dimitris Staikos | December 13, 2007 at 9:03 pm | Reply I use the following approach which I think is clearer, more easily extensible and m
Practice Problems Quizzes Resources Source Code Source Code Snippets C and C++ Tips Finding a Job References Function Reference Syntax Reference Programming FAQ Getting Help Message Board Email About Us When To Use Goto When Programming in C By Alex Allain Although the use of goto is almost always bad programming practice (surely you can find a better way of doing XYZ), there are times when it really isn't a bad choice. Some might even argue that, when it is useful, it's the best choice. Most of what I have to say about goto really only applies to C. If you're using C++, there's no sound reason to use goto in place of exceptions. In C, however, you don't have the power of an exception handling mechanism, so if you want to separate out error handling from the rest of your program logic, and you want to avoid rewriting clean up code multiple times throughout your code, then goto can be a good choice. What do I mean? You might have some code that looks like this: int big_function() { /* do some work */ if([error]) { /* clean up*/ return [error]; } /* do some more work */ if([error]) { /* clean up*/ return [error]; } /* do some more work */ if([error]) { /* clean up*/ return [error]; } /* do some more work */ if([error]) { /* clean up*/ return [error]; } /* clean up*/ return [success]; } This is fine until you realize that you need to change your cleanup code. Then you have to go through and make 4 changes. Now, you might decide that you can just encapsulate all of the cleanup into a single function; that's not a bad idea. But it does mean that you'll need to be careful with pointers -- if you plan to free a pointer in your cleanup function, there's no way to set it to then point to NULL unless you pass in a pointer to a pointer. In a lot of cases, you won't be using that pointer again anyway, so that may not be a major conce