Yes, two hated constructs combined. Is it as bad as it sounds or can it be seen as a good way to control usage of goto and also provide a reasonable cleanup strategy?
At work we had a discussion about whether or not to allow goto in our coding standard. In general nobody wanted to allow free usage of goto but some were positive about using it for cleanup jumps. As in this code:
void func()
{
char* p1 = malloc(16);
if( !p1 )
goto cleanup;
char* p2 = malloc(16);
if( !p2 )
goto cleanup;
goto norm_cleanup;
err_cleanup:
if( p1 )
free(p1);
if( p2 )
free(p2);
norm_cleanup:
}
The abovious benefit of such use is that you don't have to end up with this code:
void func()
{
char* p1 = malloc(16);
if( !p1 ){
return;
}
char* p2 = malloc(16);
if( !p2 ){
free(p1);
return;
}
char* p3 = malloc(16);
if( !p3 ){
free(p1);
free(p2);
return;
}
}
Especially in constructor-like functions with many allocations this can sometimes grow very bad, not the least when someone has to insert something in the middle.
So, in order to be able to use goto, but still clearly isolate it from being used freely, a set of flow controlling macros was created for handling the task. Looks something like this (simplified):
#define FAIL_SECTION_BEGIN int exit_code[GUID] = 0;
#define FAIL_SECTION_DO_EXIT_IF( cond, exitcode ) if(cond){exit_code[GUID] = exitcode; goto exit_label[GUID];}
#define FAIL_SECTION_ERROR_EXIT(code) exit_label[GUID]: if(exit_code[GUID]) int code = exit_code[GUID];else goto end_label[GUID]
#define FAIL_SECTION_END end_label[GUID]:
We can use this as follows:
int func()
{
char* p1 = NULL;
char* p2 = NULL;
char* p3 = NULL;
FAIL_SECTION_BEGIN
{
p1 = malloc(16);
FAIL_SECTION_DO_EXIT_IF( !p1, -1 );
p2 = malloc(16);
FAIL_SECTION_DO_EXIT_IF( !p2, -1 );
p3 = malloc(16);
FAIL_SECTION_DO_EXIT_IF( !p3, -1 );
}
FAIL_SECTION_ERROR_EXIT( code )
{
if( p3 )
free(p3);
if( p2 )
free(p2);
if( p1 )
free(p1);
return code;
}
FAIL_SECTION_END
return 0;
It looks nice, and comes with many benefits, BUT, are there any drawbacks we should be thinking about before rolling this out into development? It is after all very flow controlling and goto:ish. Both are discouraged. What are the arguments for discouraging them in this case?
Thanks.
Error handling is one of the rare situations when goto is not so bad.
But if I had to maintain that code I would be very upset that goto are hidden by macros.
So in this case goto is OK for me but not macros.
Using goto to go to a common error handler/cleanup/exit sequence is absolutely fine.
This code:
void func()
{
char* p1 = malloc(16);
if( !p1 )
goto cleanup;
char* p2 = malloc(16);
if( !p2 )
goto cleanup;
cleanup:
if( p1 )
free(p1);
if( p2 )
free(p2);
}
can be legally written as:
void func()
{
char* p1 = malloc(16);
char* p2 = malloc(16);
free(p1);
free(p2);
}
whether or not the memory allocations succeed.
This works because free() does nothing if passed a NULL pointer. You can use the same idiom when designing your own APIs to allocate and free other resources:
// return handle to new Foo resource, or 0 if allocation failed
FOO_HANDLE AllocFoo();
// release Foo indicated by handle, - do nothing if handle is 0
void ReleaseFoo( FOO_HANDLE h );
Designing APIs like this can considerably simplify resource management.
Cleanup with goto is a common C idiom and is used in Linux kernel*.
**Perhaps Linus' opinion is not the best example of a good argument, but it does show goto being used in a relatively large scale project.*
If the first malloc fails you then cleanup both p1 and p2. Due to the goto, p2 is not initialised and may point to anything. I ran this quickly with gcc to check and attempting to free(p2) would indeed cause a seg fault.
In your last example the variables are scoped inside the braces (i.e. they only exist in the FAIL_SECTION_BEGIN block).
Assuming the code works without the braces you'd still have to initialise all the pointers to NULL before FAIL_SECTION_BEGIN to avoid seg faulting.
I have nothing against goto and macros but I prefer Neil Butterworth's idea..
void func(void)
{
void *p1 = malloc(16);
void *p2 = malloc(16);
void *p3 = malloc(16);
if (!p1 || !p2 || !p3) goto cleanup;
/* ... */
cleanup:
if (p1) free(p1);
if (p2) free(p2);
if (p3) free(p3);
}
Or if it's more appropriate..
void func(void)
{
void *p1 = NULL;
void *p2 = NULL;
void *p3 = NULL;
p1 = malloc(16);
if (!p1) goto cleanup;
p2 = malloc(16);
if (!p2) goto cleanup;
p3 = malloc(16);
if (!p3) goto cleanup;
/* ... */
cleanup:
if (p1) free(p1);
if (p2) free(p2);
if (p3) free(p3);
}
The term "Structured Programming" which we all know as the anti-goto thing originally started and developed as a bunch of coding patterns with goto's (or JMP's). Those patterns were called the while and if patterns, amongst others.
So, if you are using goto's, use them in a structured way. That limits the damage. And those macro's seem a reasonable approach.
The original code would benefit from using multiple return statements - there is no need to hop around the error return clean up code. Plus, you normally need the allocated space released on an ordinary return too - otherwise you are leaking memory. And you can rewrite the example without goto if you are careful. This is a case where you can usefully declare variables before otherwise necessary:
void func()
{
char *p1 = 0;
char *p2 = 0;
char *p3 = 0;
if ((p1 = malloc(16)) != 0 &&
(p2 = malloc(16)) != 0 &&
(p3 = malloc(16)) != 0)
{
// Use p1, p2, p3 ...
}
free(p1);
free(p2);
free(p3);
}
When there are non-trivial amounts of work after each allocation operation, then you can use a label before the first of the free() operations, and a goto is OK - error handling is the main reason for using goto these days, and anything much else is somewhat dubious.
I look after some code which does have macros with embedded goto statements. It is confusing on first encounter to see a label that is 'unreferenced' by the visible code, yet that cannot be removed. I prefer to avoid such practices. Macros are OK when I don't need to know what they do - they just do it. Macros are not so OK when you have to know what they expand to to use them accurately. If they don't hide information from me, they are more of a nuisance than a help.
Illustration - names disguised to protect the guilty:
#define rerrcheck if (currval != &localval && globvar->currtub && \
globvar->currtub->te_flags & TE_ABORT) \
{ if (globvar->currtub->te_state) \
globvar->currtub->te_state->ts_flags |= TS_FAILED;\
else \
delete_tub_name(globvar->currtub->te_name); \
goto failure; \
}
#define rgetunsigned(b) {if (_iincnt>=2) \
{_iinptr+=2;_iincnt-=2;b = ldunsigned(_iinptr-2);} \
else {b = _igetunsigned(); rerrcheck}}
There are several dozen variants on rgetunsigned() that are somewhat similar - different sizes and different loader functions.
One place where these are used contains this loop - in a larger block of code in a single case of a large switch with some small and some big blocks of code (not particularly well structured):
for (i = 0 ; i < no_of_rows; i++)
{
row_t *tmprow = &val->v_coll.cl_typeinfo->clt_rows[i];
rgetint(tmprow->seqno);
rgetint(tmprow->level_no);
rgetint(tmprow->parent_no);
rgetint(tmprow->fieldnmlen);
rgetpbuf(tmprow->fieldname, IDENTSIZE);
rgetint(tmprow->field_no);
rgetint(tmprow->type);
rgetint(tmprow->length);
rgetlong(tmprow->xid);
rgetint(tmprow->flags);
rgetint(tmprow->xtype_nm_len);
rgetpbuf(tmprow->xtype_name, IDENTSIZE);
rgetint(tmprow->xtype_owner_len);
rgetpbuf(tmprow->xtype_owner_name, IDENTSIZE);
rgetpbuf(tmprow->xtype_owner_name,
tmprow->xtype_owner_len);
rgetint(tmprow->alignment);
rgetlong(tmprow->sourcetype);
}
It isn't obvious that the code there is laced with goto statements! And clearly, a full exegesis of the sins of the code it comes from would take all day - they are many and varied.
The first example looks much more readable to me than the macroised version.
And mouviciel said it much better than I did
#define malloc_or_die(size) if(malloc(size) == NULL) exit(1)
It's not like you can really recover from failed malloc's unless you have software worth writing a transaction system for, if you do, add roll back code to malloc_or_die.
For a real example of good use of goto, check out parsing dispatch code that use computed goto.
Related
I noticed a strange idiom in openssl source code, here and repeated below:
if ((in == NULL) && (passwds == NULL)) {
if (1) { (* <---- HERE *)
#ifndef OPENSSL_NO_UI
/* build a null-terminated list */
static char *passwds_static[2] = { NULL, NULL };
passwds = passwds_static;
if (in == NULL)
if (EVP_read_pw_string
(passwd_malloc, passwd_malloc_size, "Password: ",
!(passed_salt || in_noverify)) != 0)
goto end;
passwds[0] = passwd_malloc;
} else {
#endif
BIO_printf(bio_err, "password required\n");
goto end;
}
}
It seems that this piece of code is equivalent to:
if ((in == NULL) && (passwds == NULL)) {
#ifndef OPENSSL_NO_UI
/* build a null-terminated list */
static char *passwds_static[2] = { NULL, NULL };
passwds = passwds_static;
if (in == NULL)
if (EVP_read_pw_string
(passwd_malloc, passwd_malloc_size, "Password: ",
!(passed_salt || in_noverify)) != 0)
goto end;
passwds[0] = passwd_malloc;
#else
BIO_printf(bio_err, "password required\n");
goto end;
#endif
}
I ruled out some explanations:
it could be to introduce block scope for passwds_static, but the enclosing if would serve a similar purpose
it could be a construct that through several meaningful transformations becomes meaningless, but that construct is there since the introduction of OPENSSL_NO_UI.
Am I missing something here? What are the advantages of this if (1)? Is this used in other code bases?
Thanks!
After looking at other similar places, I found an explanation:
if (1) { /* This is a trick we use to avoid bit rot.
* at least the "else" part will always be
* compiled.
*/
#ifdef AF_INET6
family = AF_INET6;
} else {
#endif
BIOerr(BIO_F_ACPT_STATE, BIO_R_UNAVAILABLE_IP_FAMILY);
goto exit_loop;
}
In most cases (including their CI I guess), OPENSSL_NO_UI is not defined, so both branches are compiled. If the API one of the branches uses changes, it will be spotted by the compiler, and it can be fixed, without having to test all the compile-time switches.
It is apparently used to remove code that shouldn't be executed, similar to using #ifdef compiler switches. Most often strange things like this is an indication of poor version control more than anything else.
Please note that this is not recommended practice - there should be no source code present that never gets executed in no circumstances. It is better practice to use version control, or if that's not possible use compiler switches, or if that's not possible then "comment out" the code.
the statement:
if(1) {
is to always have a matching opening brace for the closing brace.
First of all, if someone can reword the question to make it clearer, please do.
A common occurrence in C programming is having several resources to be initialized/allocated in a specific order. Each resource is a prerequisite for the initialization of subsequent ones. If one of the steps fails, the leftover resources from previous steps must be de-allocated. Ideal pseudocode (utilizing a magically generic pure-unobtainium clean_up_and_abort() function) would look approximately as follows:
err=alloc_a()
if(err)
clean_up_and_abort()
err=alloc_b()
if(err)
clean_up_and_abort()
err=alloc_c()
if(err)
clean_up_and_abort()
// ...
profit()
I have seen several ways of handling this, all of them seem to have significant drawbacks, at least in terms of what people tend to consider "good practice".
What is are the most readable and least error-prone ways of structuring the code when handling this situation? Efficiency is preferred, but a reasonable amount of efficiency can be sacrificed for the sake of readability. Please list advantages and drawbacks. Answers discussing multiple methods are welcome.
The goal is to hopefully end up with a set of several preferred methods for solving this problem.
I'll start with some of the methods I've already seen, please comment on them and add others.
The three most common methods I've seen so far:
1: Nested if-statements (without multiple returns for the SESE purists). With a long chain of prerequisites, this gets out of hand fast. IMO, even in simple cases this is a readability disaster and has no real advantages. I am including it because I see people do this (too) often.
uint32_t init_function() {
uint32_t erra, errb, errc, status;
A *a;
B *b;
C *c;
erra = alloc_a(&a);
if(erra) {
status = INIT_FAIL_A;
} else {
errb = alloc_b(&b);
if(errb) {
dealloc_a(&a);
status = INIT_FAIL_B;
} else {
errc = alloc_c();
if(errc) {
dealloc_b(&b);
dealloc_a(&a);
status = INIT_FAIL_C;
} else {
profit(a,b,c);
status = INIT_SUCCESS;
}
}
}
// Single return.
return status;
}
2: Multiple returns. This is my preferred method right now. THe logic is easy to follow but it's still dangerous because cleanup code has to be duplicated and it's easy to forget to deallocate something in one of the cleanup sections.
uint32_t init_function() {
uint32_t err;
A *a;
B *b;
C *c;
err = alloc_a(&a);
if(err) {
return INIT_FAIL_A;
}
err = alloc_b(&b);
if(err) {
dealloc_a(&a);
return INIT_FAIL_B;
}
err = alloc_c(&c);
if(err) {
dealloc_b(&b);
dealloc_a(&a);
return INIT_FAIL_C;
}
profit(a,b,c);
return INIT_SUCCESS;
}
3: GOTO. Many people don't like goto on principle, but this is one of the standard arguments for a valid use of goto in C programming. The advantage is that it's hard to forget a cleanup step and there is no copypasting.
uint32_t init_function() {
uint32_t status;
uint32_t err;
A *a;
B *b;
C *c;
err = alloc_a(&a);
if(err) {
status = INIT_FAIL_A;
goto LBL_FAIL_A;
}
err = alloc_b(&b);
if(err) {
status = INIT_FAIL_B;
goto LBL_FAIL_B;
}
err = alloc_c(&c);
if(err) {
status = INIT_FAIL_C;
goto LBL_FAIL_C;
}
profit(a,b,c);
status = INIT_SUCCESS;
goto LBL_SUCCESS;
LBL_FAIL_C:
dealloc_b(&b);
LBL_FAIL_B:
dealloc_a(&a);
LBL_FAIL_A:
LBL_SUCCESS:
return status;
}
Anything else I did not mention?
4: Global variables, woohoo!!! Because everybody loves global variables, just like they love goto. But seriously, if you limit the scope of the variables to file scope (using the static keyword) then it's not that bad. Side note: the cleanup function takes/returns the error code unchanged, so as to declutter the code in the init_function.
static A *a = NULL;
static B *b = NULL;
static C *c = NULL;
uint32_t cleanup( uint32_t errcode )
{
if ( c )
dealloc_c(&c);
if ( b )
dealloc_b(&b);
if ( a )
dealloc_a(&a);
return errcode;
}
uint32_t init_function( void )
{
if ( alloc_a(&a) != SUCCESS )
return cleanup(INIT_FAIL_A);
if ( alloc_b(&b) != SUCCESS )
return cleanup(INIT_FAIL_B);
if ( alloc_c(&c) != SUCCESS )
return cleanup(INIT_FAIL_C);
profit(a,b,c);
return INIT_SUCCESS;
}
5: Faux OOP. For those who can't handle the truth (that global variables are actually useful in C programs), you can take the C++ approach. C++ takes all of the global variables, puts them into a structure, and calls them "member" variables. And somehow that makes everybody happy.
The trick is to pass a pointer to the structure to all of the functions, as the first argument. C++ does this behind the scenes, in C you have to do it explicitly. I call the pointer that so as to avoid conflicts/confusion with this.
// define a class (uhm, struct) with status, a cleanup method, and other stuff as needed
typedef struct stResources
{
char *status;
A *a;
B *b;
C *c;
void (*cleanup)(struct stResources *that);
}
stResources;
// the cleanup method frees all resources, and frees the struct
void cleanup( stResources *that )
{
if ( that->c )
dealloc_c( &that->c );
if ( that->b )
dealloc_b( &that->b );
if ( that->a )
dealloc_a( &that->a );
free( that );
}
// the init function returns a pointer to the struct, or NULL if the calloc fails
// the status member variable indicates whether initialization succeeded, NULL is success
stResources *init_function( void )
{
stResources *that = calloc( 1, sizeof(stResources) );
if ( !that )
return NULL;
that->cleanup = cleanup;
that->status = "Item A is out to lunch";
if ( alloc_a( &that->a ) != SUCCESS )
return that;
that->status = "Item B is never available when you need it";
if ( alloc_b( &that->b ) != SUCCESS )
return that;
that->status = "Item C is being hogged by some other process";
if ( alloc_c( &that->c ) != SUCCESS )
return that;
that->status = NULL; // NULL is success
return that;
}
int main( void )
{
// create the resources
stResources *resources = init_function();
// use the resources
if ( !resources )
printf( "Buy more memory already\n" );
else if ( resources->status != NULL )
printf( "Uhm yeah, so here's the deal: %s\n", resources->status );
else
profit( resources->a, resources->b, resources->c );
// free the resources
if ( resources )
resources->cleanup( resources );
}
6. setjmp() and longjmp() for simulating exceptions and scoped flow control.
Please don’t do this.
For example,
I need to malloc two pieces of memory, so:
void *a = malloc (1);
if (!a)
return -1;
void *b = malloc (1);
if (!b)
{
free (a);
return -1;
}
Notice if the second malloc fails, I have to free "a" first. The problem is, this can be very messy if there are many such malloc's and error checking's, unless I use the notorious "goto" clause and carefully arrange the order of free's along with the labels:
void *a = malloc (1);
if (!a)
goto X;
void *b = malloc (1);
if (!b)
goto Y;
return 0; //normal exit
Y:
free (a);
X:
return -1;
Do you have any better solution to this situation? Thanks in advance.
We do like this:
void *a = NULL;
void *b = NULL;
void *c = NULL;
a = malloc(1);
if (!a) goto errorExit;
b = malloc(1);
if (!b) goto errorExit;
c = malloc(1);
if (!b) goto errorExit;
return 0;
errorExit:
//free a null pointer is safe.
free(a);
free(b);
free(c);
return -1;
Using goto is not a bad thing, in my opinion. Using it for resource cleanup is just right for it.
Source code as famous as the Linux kernel uses the technique.
Just don't use goto to go backwards. That leads to disaster and confusion. Only jump forward is my recommendation.
As previously mentioned by Zan Lynx use goto statement.
You can also alloc larger chunk of memory for further use.
Or you can invest your time to develop something like memory pool.
Or do this.
void *a,*b;
char * p = malloc(2);
if (!p) return -1;
a = p;
b = p+1;
I think OOP techniques could give you a nice and clean solution to this problem:
typedef struct {
void *a;
void *b;
} MyObj;
void delete_MyObj(MyObj* obj)
{
if (obj) {
if (obj->a)
free(obj->a);
if (obj->b)
free(obj->b);
free(obj);
}
}
MyObj* new_MyObj()
{
MyObj* obj = (MyObj*)malloc(sizeof(MyObj));
if (!obj) return NULL;
memset(obj, 0, sizeof(MyObj));
obj->a = malloc(1);
obj->b = malloc(1);
if (!obj->a || !obj->b) {
delete_MyObj(obj);
return 0;
}
return obj;
}
int main()
{
MyObj* obj = new_MyObj();
if (obj) {
/* use obj */
delete_MyObj(obj);
}
}
There's nothing really wrong with your goto code IMO (I'd use more verbose labels).
In this case though, the goto statements you've written create exactly the same structure as reversing the ifs.
That is, a conditional forward goto that doesn't leave any scope does exactly the same as an if statement with no else. The difference is that the goto happens not to leave scope, whereas the if is constrained not to leave scope. That's why the if is usually easier to read: the reader has more clues up front.
void *a = malloc (1);
if (a) {
void *b = malloc (1);
if (b) {
return 0; //normal exit
}
free(a);
}
return -1;
For a couple of levels this is OK, although taken too far you get "arrow code" with too many levels of indentation. That becomes unreadable for entirely different reasons.
Use a garbage collector like boehmgc.
It works, it's easy to use, there is no slowdown contrary to common opinion.
https://en.wikipedia.org/wiki/Boehm_garbage_collector
http://www.hpl.hp.com/personal/Hans_Boehm/gc/
Depending on the platform / context your application is running, continuing after malloc() is returning NULL may not make much sense anymore.
So in many cases, a simple
if (!a)
catastrophic_failure_bail_out();
might be the most sensible solution and keep the code clean and readable.
I'm trying to write a function in C to solve a math problem. In that function, there are several steps, and each step needs to allocate some memory with the size depending on the calculation results in previous steps (so I can't allocate them all at the beginning of the function). The pseudo code looks like:
int func(){
int *p1, *p2, *p3, *p4;
...
p1 = malloc(...);
if(!p1){
return -1; //fail in step 1
}
...
p2 = malloc(...);
if(!p2){
free(p1);
return -2; //fail in step 2
}
...
p3 = malloc(...);
if(!p3){
free(p1);
free(p2);
return -3; //fail in step 3
}
...
p4 = malloc(...);
if(!p4){
free(p1);
free(p2);
free(p3); /* I have to write too many "free"s here! */
return -4; //fail in step 4
}
...
free(p1);
free(p2);
free(p3);
free(p4);
return 0; //normal exit
}
The above way to handle malloc failures is so ugly. Thus, I do it in the following way:
int func(){
int *p1=NULL, *p2=NULL, *p3=NULL, *p4=NULL;
int retCode=0;
...
/* other "malloc"s and "if" blocks here */
...
p3 = malloc(...);
if(!p3){
retCode = -3; //fail in step 3
goto FREE_ALL_EXIT;
}
...
p4 = malloc(...);
if(!p4){
retCode = -4; //fail in step 4
goto FREE_ALL_EXIT;
}
...
FREE_ALL_EXIT:
free(p1);
free(p2);
free(p3);
free(p4);
return retCode; //normal exit
}
Although I believe it's more brief, clear, and beautiful now, my team mate is still strongly against the use of 'goto'. And he suggested the following method:
int func(){
int *p1=NULL, *p2=NULL, *p3=NULL, *p4=NULL;
int retCode=0;
...
do{
/* other "malloc"s and "if" blocks here */
p4 = malloc(...);
if(!p4){
retCode = -4; //fail in step 4
break;
}
...
}while(0);
free(p1);
free(p2);
free(p3);
free(p4);
return retCode; //normal exit
}
Hmmm, it seems a way to avoid the use of 'goto', but this way increases indents, which makes the code ugly.
So my question is, is there any other method to handle many 'malloc' failures in a good code style? Thank you all.
goto in this case is legitimate. I see no particular advantage to the do{}while(0) block as its less obvious what pattern it is following.
First of all, there's nothing wrong with goto—this is a perfectly legitimate use of goto. The do { ... } while(0) with break statements are just gotos in disguise, and it only serves to obfuscate the code. Gotos are really the best solution in this case.
Another option is to put a wrapper around malloc (e.g. call it xmalloc) which kills the program if malloc fails. For example:
void *xmalloc(size_t size)
{
void *mem = malloc(size);
if(mem == NULL)
{
fprintf(stderr, "Out of memory trying to malloc %zu bytes!\n", size);
abort();
}
return mem;
}
Then use xmalloc everywhere in place of malloc, and you no longer need to check the return value, since it will return a valid pointer if it returns at all. But of course, this is only usable if you want allocation failures to be an unrecoverable failure. If you want to be able to recover, then you really do need to check the result of every allocation (though honestly, you'll probably have another failure very soon after).
Ask your teammate how he would re-write this sort of code:
if (!grabResource1()) goto res1failed;
if (!grabResource2()) goto res2failed;
if (!grabResource3()) goto res3failed;
(do stuff)
res3failed:
releaseResource2();
res2failed:
releaseResource1();
res1failed:
return;
And ask how he would generalize it to n resources. (Here, "grabbing a resource" could mean locking a mutex, opening a file, allocating memory, etc. The "free on NULL is OK" hack does not solve everything...)
Here, the alternative to goto is to create a chain of nested functions: Grab a resource, call a function that grabs another resource and calls another function that grabs a resource and calls another function... When a function fails, its caller can free its resource and return failure, so the releasing happens as the stack unwinds. But do you really think this is easier to read than the gotos?
(Aside: C++ has constructors, destructors, and the RAII idiom to handle this sort of thing. But in C, this is the one case where goto is clearly the right answer, IMO.)
There's nothing wrong with goto in error handling and there's actually no code difference between using a do { ... } while(0); with breaks; instead of goto (since they're both jmp instructions). I would say that seems normal. One thing you could do that is shorter is create an array of int * types and iterate through while calling malloc. If one fails free the ones that are non-null and return an error code. This is the cleanest way I can think of so something like
int *arr[4];
unsigned int i;
for (i = 0; i < 4; ++i)
if (!(arr[i] = malloc(sizeof(int))) {
retCode = -(i + 1); //or w/e error
break;
}
if (errorCode)
for (i = 0; i < 4; i++)
if (arr[i])
free(arr[i]);
else
break;
or something along those lines (used brain compiler for this so I might be wrong)
Not only does this shorten your code but also avoids goto's (which I don't see anything wrong with) so you and your teammate can both be happy :D
David Hanson wrote the book C Interfaces and Implementations: Techniques for Creating Reusable Software. His Mem interface provides functions that are "similar to those in the standard C library, but they don't accept zero sizes and never return null pointers." The source code includes a production implementation and a checking implementation.
He also implements an Arena interface. The Arena interface releases you from the obligation to call free() for every malloc(). Instead, there's just a single call to free the entire arena.
CII source code
If an allocation fails, simply assign the error code as normal. conditionalize each malloc like so:
if (retCode < 0) malloc...
and then at the end of your code, add this:
int * p_array[] = { p1, p2, p3, p4};
for (int x = -retCode + 1; x >= 0; x-- )
{
free(p_array[x]);
}
What is the best way to manage resources for a C program. Should I use a nested if structure or should I use goto statements?
I am aware there is a lot of taboo about goto statements. However, I think it is justified for local resource clean up. I have supplied two samples. One compares a nested if structure and another uses goto statements. I personally find the goto statements make the code easier to read. For those who might argue that the nested if prompt better structure, imagine if the datatype was something other than a char*, like a Windows handle. I feel that the nested if structure would get out of hand with a series of CreateFile functions or any other function that takes large quantities of parameters.
This article demonstrates that local goto statements create RAII for C code. The code is neat an easy to follow. Imagine that as a series of nested if statements.
I understand that goto is taboo in many other languages because their exists other control mechanisms like try/catch etc, however, in C it seems appropriate.
#include <stdlib.h>
#define STRING_MAX 10
void gotoExample()
{
char *string1, *string2, *string3, *string4, *string5;
if ( !(string1 = (char*) calloc(STRING_MAX, sizeof(char))) )
goto gotoExample_string1;
if ( !(string2 = (char*) calloc(STRING_MAX, sizeof(char))) )
goto gotoExample_string2;
if ( !(string3 = (char*) calloc(STRING_MAX, sizeof(char))) )
goto gotoExample_string3;
if ( !(string4 = (char*) calloc(STRING_MAX, sizeof(char))) )
goto gotoExample_string4;
if ( !(string5 = (char*) calloc(STRING_MAX, sizeof(char))) )
goto gotoExample_string5;
//important code goes here
gotoExample_string5:
free(string4);
gotoExample_string4:
free(string3);
gotoExample_string3:
free(string2);
gotoExample_string2:
free(string1);
gotoExample_string1:
}
void nestedIfExample()
{
char *string1, *string2, *string3, *string4, *string5;
if (string1 = (char*) calloc(STRING_MAX, sizeof(char)))
{
if (string2 = (char*) calloc(STRING_MAX, sizeof(char)))
{
if (string3 = (char*) calloc(STRING_MAX, sizeof(char)))
{
if (string4 = (char*) calloc(STRING_MAX, sizeof(char)))
{
if (string5 = (char*) calloc(STRING_MAX, sizeof(char)))
{
//important code here
free(string5);
}
free(string4);
}
free(string3);
}
free(string2);
}
free(string1);
}
}
int main(int argc, char* argv[])
{
nestedIfExample();
gotoExample();
return 0;
}
I would also like to quote Linus Torvalds on goto statements inside the Linux Kernel.
And sometimes structure is bad, and
gets into the way, and using a "goto"
is just much clearer.
For example, it is quite common to
have conditionals THAT DO NOT NEST.
In which case you have two
possibilities
use goto, and be happy, since it doesn't enforce nesting
This makes the code more readable,
since the code just does what the
algorithm says it should do.
duplicate the code, and rewrite it in a nesting form so that you can
use the structured jumps.
This often makes the code much LESS
readable, harder to maintain, and
bigger.
The Pascal language is a prime example
of the latter problem. Because it
doesn't have a "break" statement,
loops in (traditional) Pascal end up
often looking like total shit, because
you have to add totally arbitrary
logic to say "I'm done now".
Is goto acceptable for resource management? Should I use nested if statements or is there a better way?
Update: Examples of Good Gotos In C
No doubt about it Dijkstra was a formidable personality in the programming world. His
Goto Considered Harmful paper
was way overblown. Yes GoTo may be used indiscriminately and can be harmfull but many think an outright ban on GoTo is
not warranted. Knuth provided a very well reasoned rebuttal to Dijkstra in:
Structured Programming with GO TOs
Read Knuth's paper, you will find that your GoTo pattern is one of the good uses
for GoTo.
BTW, Dijkstra is very quotable for a number of other things too. How about:
Object-oriented programming is an exceptionally bad idea which could only have originated in California.
Dijkstra was a great mathematician and made huge contributions to computer science. However, I don't
think he had to deal with, or was interested in, the day to day type stuff that 99.99 percent of
our programs do.
Use GoTo only with reason and structure. Use them rarely. But do use them.
If by using goto you can avoid writing complex code, then use goto.
Your example could also be written like this (no gotos):
void anotherExample()
{
char *string1, *string2, *string3, *string4, *string5;
string1 = string2 = string3 = string4 = string5 = 0;
if ((string1 = (char*) calloc(STRING_MAX, sizeof(char)))
&& (string2 = (char*) calloc(STRING_MAX, sizeof(char)))
&& (string3 = (char*) calloc(STRING_MAX, sizeof(char)))
&& (string4 = (char*) calloc(STRING_MAX, sizeof(char)))
&& (string5 = (char*) calloc(STRING_MAX, sizeof(char))))
{
//important code here
}
free(string1);
free(string2);
free(string3);
free(string4);
free(string5);
}
Cleanup using goto has the advantage that it's less error-prone. Having to free each and every resource allocated on each and every return point can lead to someone someday missing some cleanup when doing maintenance work.
That said, I'll quote Knuth's "Structured Programming with goto Statements":
I argue for the elimination of go to's in certain cases, and for their introduction in others.
and Knuth's quote of Dijkstra in that same paper:
"Please don't fall into the trap of believing that I am terribly dogmatical about [the go to statement]. I have the uncomfortable feeling that others are making a religion out of it, as if the conceptual problems of programming could be solved by a single trick, by a simple form of coding discipline!" [29].
Always use one goto in each program to annoy the purists
That's my philosophy.
Seriously, on some occasions a goto is reasonable, especially if it just does something obvious like jump to common return code at the bottom of a function.
Personally I have used goto in this manner in the past. People hate it because it reminds them of the spaghetti code they used to write/maintain, or because someone who wrote/maintaned such code beat the concept that gotos are evil into them.
You probably could write something decent without goto, sure. But it's not going to do any harm in this kind of circumstance.
From two of your alternatives, goto is naturally better and nicer. But there's a third and better alternative: Use recursion!
I'd structure the code differently than either one. Unless I had some outstanding reason to do otherwise, I'd probably write the code something like this:
char *strings[5] = {NULL};
int all_good = 1;
for (i=0; i<5 && all_good; i++) {
strings[i] = malloc(STRING_MAX);
all_good &= strings[i] != NULL;
}
if (all_good)
important_code();
for (int i=0; i<5; i++)
free(strings[i]);
One very big difference between the example in the article you link to and the code you're posting is that your gotos labels are <functionName>_<number> and their goto labels are cleanup_<thing_to_cleanup>.
You're be using goto line_1324 next, and the code will get edited so the line_1234 label is on line 47823 ...
Use it like the example, and be very careful to write code to be read.
If you know what you're doing, and the resulting code looks cleaner and more readable (I bet it does), then there's absolutely no problem with using goto's. Especially the 'graceful initialization failure recovery' example you showed is widely used.
BTW, when writing structured code that initializes 100 things, you'd need 100 levels of indentation...which is plain ugly.
In C, goto is often the only way to approximate cleanup code like C++ destructors or Java finally clauses. Since it's really the best tool you have for that purpose, I say use it. Yeah, it's easy to abuse, but so are a lot of programming constructs. For example, most Java programmers wouldn't hesitate to throw exceptions, but exceptions are also easy to abuse if they're used for something other than error reporting, such as flow control. But if you're using goto explicitly for the purpose of avoiding cleanup-code duplication, then it's safe to say you're probably not abusing it.
You can find many perfectly reasonable uses of goto in the Linux kernel, for example.
For me, I prefer this style of goto error handling. Taking Nick D's snippet one step further, it uses one general goto label for error handling.
void gotoExample()
{
char *string1, *string2, *string3, *string4, *string5;
string1 = string2 = string3 = string4 = string5 = NULL;
if ( !(string1 = (char*) calloc(STRING_MAX, sizeof(char))) )
goto HANDLE_ERROR;
if ( !(string2 = (char*) calloc(STRING_MAX, sizeof(char))) )
goto HANDLE_ERROR;
if ( !(string3 = (char*) calloc(STRING_MAX, sizeof(char))) )
goto HANDLE_ERROR;
if ( !(string4 = (char*) calloc(STRING_MAX, sizeof(char))) )
goto HANDLE_ERROR;
if ( !(string5 = (char*) calloc(STRING_MAX, sizeof(char))) )
goto HANDLE_ERROR;
//important code goes here
HANDLE_ERROR:
if (string5)
free(string5);
if (string4)
free(string4);
if (string3)
free(string3);
if (string2)
free(string2);
if (string1)
free(string1);
}