There is no any need to use the goto statement. And in general forget the goto statement.:) And also try to use one return statement from a function if possible:) Numerous return statements make a function less readable with redundant and duplicated code.
Instead you can write for example
struct Obj * obj_create( void )
{
struct Obj *obj = NULL;
if ( ( obj = malloc(sizeof(struct Obj) ) != NULL )
{
if ( !(obj->child_obj1 = child_obj1_create() ) ||
!(obj->child_obj2 = child_obj2_create() ) )
{
free( obj->child_obj1 );
free( obj );
obj = NULL;
}
}
return obj;
}
The function free may be called for a null pointer.
Or instead of this statement
free( obj->child_obj1 );
you could write if you want
if ( obj->child_obj1 ) free( obj->child_obj1 );
As you can see the function is compact and clear.
Within the function you can use your function obj_destroy that should be rewritten again without numerous return statements like
void obj_destroy( struct Obj *obj )
{
if ( obj )
{
if ( obj->child_obj2 ) free( obj->child_obj2 );
if ( obj->child_obj1 ) free( obj->child_obj1 );
free(obj);
}
}
the following way
struct Obj * obj_create( void )
{
struct Obj *obj = NULL;
if ( ( obj = malloc(sizeof(struct Obj) ) != NULL )
{
obj->child_obj2 = NULL;
if ( !(obj->child_obj1 = child_obj1_create() ) ||
!(obj->child_obj2 = child_obj2_create() ) )
{
obj_destroy( obj );
obj = NULL;
}
}
return obj;
}
I'll demonstrate why the approach with the goto statement should be thrown in the trash.:)
Let's assume that requirements for the task was changed and now instead of the two data members of the pointer types you have to use an array of pointers of one pointer type like
enum { N = 5 };
struct Obj {
struct ChildObj *child_obj[N];
};
In this case if to use the goto statement freeing the already allocated memory will look something like
err6:
free(obj->child_obj[3]);
err5:
free(obj->child_obj[2]);
err4:
free(obj->child_obj[1]);
err3:
free(obj->child_obj[0]);
err2:
free(obj);
err1:
return NULL;
Do you like such a code?:)
Actually the function in this case can look the following way based on the logic and the structure of the original function shown above:
struct Obj * obj_create( void )
{
struct Obj *obj = NULL;
if ( ( obj = malloc(sizeof(struct Obj) ) != NULL )
{
int i = 0;
while ( i < N && ( obj->child_obj[i] = child_obj_create() ) != NULL )
{
++i;
}
if ( i != N )
{
while ( i-- != 0 ) free( obj->child_obj[i] );
free( obj );
obj = NULL;
}
}
return obj;
}
As you can see its structure and logic are similar to the structure and logic of the first original function shown by me. In fact this if statement in the original function
if ( !(obj->child_obj1 = child_obj1_create() ) ||
!(obj->child_obj2 = child_obj2_create() ) )
simulates the while loop in the preceding function. The only difference is that there is no used an index because the original structure has no array. But the logic and the structure of the original function are preserved in the modified function.:)
And this single statement
free( obj->child_obj1 );
substitutes for this while loop in the modified function
while ( i-- != 0 ) free( obj->child_obj[i] );
Or vice versa if you had the modified function and needed to use two data members of different types instead of an array you can easily get the original function with the same logic and without any goto statement.:)
If to use within the function the function obj_destroy that in this case will look the following way
void obj_destroy( struct Obj *obj )
{
if ( obj )
{
for ( int i = 0; i < N && obj->child_obj[i] != NULL; i++ )
{
free( obj->child_obj[i] );
}
free(obj);
}
}
then the function obj_create will look as
struct Obj * obj_create( void )
{
struct Obj *obj = NULL;
if ( ( obj = malloc(sizeof(struct Obj) ) != NULL )
{
int i = 0;
while ( i < N && ( obj->child_obj[i] = child_obj_create() ) != NULL )
{
++i;
}
if ( i != N )
{
obj_destroy( obj );
obj = NULL;
}
}
return obj;
}
Note: On the other hand, I would like to make a remark. It is unclear why together with allocating memory referenced by the pointer obj you are also trying to allocate memory for data members obj->child_obj1 and obj->child_obj2. Maybe you should write
struct Obj * obj_create( void )
{
struct Obj *obj = malloc( sizeof( struct Obj ) );
if ( obj != NULL )
{
obj->child_obj1 = NULL;
obj->child_obj2 = NULL;
}
return obj;
}
and allocate memory referenced by the data members obj->child_obj1 and obj->child_obj2 separately when it will be required for each data member.