开发者

Neat way to handle malloc error without checking if NULL has been returned after every single malloc call?

In my code almost every function has one or more mall开发者_Go百科oc calls, and each time I have to do something like:

char *ptr = (char *)malloc(sizeof(char) * some_int);
if (ptr == NULL) {
    fprintf(stderr, "failed to allocate memory.\n");
    return -1;
}

that's four extra lines of code and if I add them everytime after I use a malloc, the length of my code will increase a lot.. so is there an elegant way to deal with this?

Thank you so much!!


There isn't usually much point in trying to stumble on when all memory is consumed. Might as well call it quits:

char* allocCharBuffer(size_t numberOfChars) 
{
    char *ptr = (char *)malloc(sizeof(char) * numberOfChars);
    if (ptr == NULL) {
        fprintf(stderr, "failed to allocate memory.\n");
        exit(-1);
    }
    return ptr;
}


Sorry, but there's nothing you can do about that in C. Except... gasp... wrap it all in a macro which will automate the if check and allow you to write custom error-handling code each time. There, I said it.

Seriously, C isn't meant to provide conveniences like this. If you don't mind exiting the program on the spot, you can wrap it in a function of course that does exit when the allocation fails -- but that's no general solution.


You could use macros. This is cheaper than grouping this code into a function because Macros don't have the overhead a function call incurs. Macros are expanded by the preprocessor stage of compilation and can be verified by the '-E' option in gcc. Now say we have func1(), func2(), func3()

#define MY_MALLOC(_ptr,_count, _lbl) \
do { \
 if (NULL == (ptr = malloc(sizeof(char) * _count))) { \
    fprintf(stderr, "Failed to allocate memory.\n"); \
    goto _lbl; \
 } \
} while(0)

func1() {  
 char *ptr;
 MY_MALLOC(ptr,10,Error);
 ....
 ...
 return (0);
Error:
 return (1);
}


func2() {  
 char *ptr;
 MY_MALLOC(ptr,10,Error);
 ....
 ...
 return (0);
Error:
 return (1);
}


func3() {  
 char *ptr;
 MY_MALLOC(ptr,10,Error);
 ....
 ...
 return (0);
Error:
 return (1);
}

#undef MY_MALLOC


When you have no real error handling (except printing something and exiting), the simple and established solution is to define a function safe_malloc which incorporates the check. (Edit: Or, of course, a macro. Whatever rocks your boat.)


If you error condition is always that simple (print error message and return) you can rewrite to save lines.

int errmsg(const char *msg, int retval) {
    fprintf(stderr, "%s\n", msg);
    return retval;
}

if ((ptr = malloc(size)) == NULL) return errmsg("failed to allocate memory.", -1);
/* ... */
free(ptr);


C runtime should clean up any resources, including open files, buffers, and allocated data. Even so, I like to use int atexit( void(*)(void)) which will call registered functions upon a normal exit. Also exit immediately if atexit returns a non-zero value, meaning your function was not registered.

#include <stdlib.h>
void register_cleanup ( void ( *cleaner )( void ))
{
    if ( atexit ( cleaner ))
    {
        fprintf ( stderr, "Error, unable to register cleanup: %s\n",
                strerror ( errno )) ;
        exit ( EXIT_FAILURE ) ;
    }
}

Then exit on malloc failure.

#include <stdlib.h>
void *malloc_or_die ( size_t size )
{
    void *dataOut = malloc ( size ) ;
    if ( !dataOut )
    {
        fprintf ( stderr, "Error, malloc: %s\n", strerror ( errno )) ;
        exit ( EXIT_FAILURE ) ;
    }
    return dataOut ;
}
void main()
{
    register_cleanup ( cleaner_fnc ) ;
    ...
    void *data = malloc_or_die ( 42 ) ;
    do_stuff ( data ) ;
    return 0 ;
}


#define my_malloc_macro(size, ptr) do { \
    ptr = malloc(size); \
    if(!ptr) { \
        printf("malloc failed\n"); \
        return -1; \
    } \
} while(0)


Or you could use an extern.

Define a function in main.c:

 void sj_handleException(bool fatal, const char* msg, const char* libMsg){
  fprintf(stderr, msg);
  if(libMsg != NULL) fprintf(stderr, libMsg);

  if(fatal) exit(EXIT_FAILURE);    
}

Any file that mallocs memory add as you would a forward declaration:

extern void sj_handleException(bool fatal, const char* msg, const char* libMsg)

Now write malloc as:

char *ptr = (char *)malloc(sizeof(char) * some_int);
if (ptr == NULL) sj_handleException(true, "failed to allocate memory.\n", NULL);

The linkage between the place in your code where you malloc'd memory and main.c that handles the exception is generated behind the scenes by the linker; it maps calls to the function with the function even though the two exist in different source files.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜