Based on your question, a similar question I asked yesterday, and some of the answers, I did a version that tries to have the benefits of all of them.
Disclaimer: Code is GCC specific (although probably works in similar compilers such as Clang).
Usage:
int *p;
/* Want >int p[47];< */
if (mallocs(&p, 47))
goto err;
...
free(p);
Properties:
- Avoid having to type
sizeof
- Can be used inside an
if
- Returns an
int
error code
- Check for invalid input pointer:
mallocs(NULL, 47) == EINVAL
- Check for negative nmemb:
mallocs(&p, -47) == -EOVERFLOW
(and p
set to NULL
)
- Check for overflow due to high nmemb:
mallocs(&p, TOO_HIGH) == EOVERFLOW
(and p
set to NULL
)
- Check for
malloc
error: mallocs(&p, 47) == ENOMEM
(and p
set to NULL
) (correct input, but malloc fails for some reson)
- Sets
errno
on any failure (malloc shall set errno on failure, so if this is going to be in a library, it would make sense to set errno on failure too)
- I don't need a pointer to a pointer given that this is a macro, but I think a user that doesn't know this would probably be happier passing a pointer to a pointer, and keep thinking it is a function :) I would have to read the code if some tells me that (what looks like) a function call modifies a pointer without a pointer to it.
- Prevents double evaluation of arguments
- Removes the possibility that someone may cast the result of malloc
Code: (Edited: This code has problems; at the end of the answer is the fixed one)
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
/*
* int mallocs(type **restrict p, ptrdiff_t nmemb);
*/
#define mallocs(ptr, nmemb) ( \
{ \
ptrdiff_t nmemb_ = (nmemb); \
__auto_type ptr_ = (ptr); \
int err_; \
\
err_ = 0; \
if (ptr_ == NULL) { \
errno = EINVAL; \
err_ = EINVAL; \
goto ret_; \
} \
if (nmemb_ < 0) { \
*ptr_ = NULL; \
errno = EOVERFLOW; \
err_ = -EOVERFLOW; \
goto ret_; \
} \
if (nmemb_ > (PTRDIFF_MAX / (ptrdiff_t)sizeof(**ptr_)) { \
*ptr_ = NULL; \
errno = EOVERFLOW; \
err_ = EOVERFLOW; \
goto ret_; \
} \
\
*ptr_ = malloc(sizeof(**ptr_) * nmemb_); \
if (*ptr_ == NULL) \
err_ = ENOMEM; \
ret_: \
err_; \
} \
)
I named it mallocs
(malloc safe)
EDIT:
After finding that the code above can only be called once in a function, I improved it making use of an inline
. Now there is the possibility to call the function or the macro, depending on your preferences (both can be called multiple times):
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#define mallocs(ptr, nmemb) ( \
{ \
__auto_type ptr_ = (ptr); \
\
*ptr_ = mallocarray((nmemb), sizeof(**ptr_)); \
\
!(*ptr_); \
} \
)
inline
void *mallocarray(ptrdiff_t nmemb, size_t size);
inline
void *mallocarray(ptrdiff_t nmemb, size_t size)
{
if (nmemb < 0)
goto ovf;
if (nmemb > (PTRDIFF_MAX / (ptrdiff_t)size))
goto ovf;
return malloc(size * nmemb);
ovf:
errno = EOVERFLOW;
return NULL;
}
I named mallocarray()
after the BSD extension reallocarray()