Reputation: 4853
Of course it is necessary to check whether certain operations occurred as expected: calls to malloc
, fopen
, fgetc
However, sometimes adding these checks makes the code way too long - especially for very simply functions. For example, I have a function where I must open a file, read in a few parameters, and allocate memory corresponding to what was just read in.
Therefore, the code ends up looking something like:
Etc.
There appears to be quite a bit of redundancy here. At least for my simple program, if any of the above checks file, I simply report the error and return control to the operating system. The code ends up looking something like this:
if(filePointer == NULL){
perror("Error X occured");
exit(EXIT_FAILURE);
}
So, a simple few-line function turns into perhaps 20 or more lines because of this error checking. Is there some where to aggregate the determination of these errors?
Just wondering if there was something that I missed.
EDIT: For example, is there a way to interrupt the flow of program when certain events occur? I.e. if EOF is read prematurely, then jump to some function that informs the user (something like an interrupt in embedded systems).
Upvotes: 1
Views: 47
Reputation: 420
One common way to address this, at least to reduce apparent code size, is wrapping the various checks with macros: e.g.,
#define CHECK_NULL(expr) { \
if ((expr) == NULL) { \
perror("Error X"); \
exit(-1); \
} \
}
CHECK_NULL(p = malloc(size))
CHECK_NULL(filePointer = fopen("foo.txt", "r"))
As for interrupting control flow, other languages often use exceptions, which are also possible in C. However, this tends to be platform-specific and isn't usually the way it's done in practice with C.
Upvotes: 1
Reputation: 34839
This is a question that every C programmer asks at some point in his/her career. You are correct that some portions of your code will have more lines of error handling code than actual useful productive code. One technique I've used in the past to streamline error handling is to implement an error function, like this
static FILE *fpin = NULL;
static FILE *fpout = NULL;
static BYTE *buffer = NULL;
static void error( char *msg, char *name )
{
if ( msg != NULL )
{
if ( name != NULL )
fprintf( stderr, "%s: %s\n", msg, name );
else
fprintf( stderr, "%s\n", msg );
}
if ( fpin != NULL )
fclose( fpin );
if ( fpout != NULL )
fclose( fpout );
if ( buffer != NULL )
free( buffer );
exit( 1 );
}
which then gets used like this
void main( int argc, char *argv[] )
{
if ( argc != 3 )
error( "Usage: ChangeBmp infile outfile" );
if ( (fpin = fopen( argv[1], "rb" )) == NULL )
error( "Unable to open input file", argv[1] );
if ( (fpout = fopen( argv[2], "wb" )) == NULL )
error( "Unable to open output file", argv[2] );
size = sizeof( bmphead );
if ( fread( &bmphead, 1, size, fpin ) != size )
error( "Unable to read header", NULL );
size = sizeof( bmpinfo );
if ( fread( &bmpinfo, 1, size, fpin ) != size )
error( "Unable to read info", NULL );
Of course, this only works if the error function has access to all of the necessary variables. For simple, single file programs, I just make the necessary variables global. In a larger project, you might have to manage the variables more carefully.
Upvotes: 1