I have never been entirely comfortable using malloc() and free() directly. For one thing, 99% of the time that I would like to call malloc, I would prefer it to take two arguments like calloc and do the multiplication itself, instead of taking one argument. Beyond that, I've often wished that free could magically zero out the pointer it is passed (which, of course, is impossible for it to do, since its pointer argument is pass-by-value).
So I've put together a very small library of wrapper functions to address these and minor other issues I have with malloc / calloc / realloc / free / memset / memcpy. The functions in this little library are:
mem_alloc— Allocate a block of memory, given an item count and item size. This is simply amallocwrapper.mem_alloc_clear— Allocate and clear a block of memory, given an item count and item size. This is simply acallocwrapper.mem_clear— Clear a block of memory, given a pointer, item count, and item size. This is amemset/bzerowrapper.mem_copy— Copy a block of memory, given a source and destination pointer, item count, and item size. This is amemcpywrapper.mem_realloc— Reallocate a block of memory, given a pointer, item count, and item size. This is simply areallocwrapper.mem_alloc_copy— Allocate a block of memory and copy memory into it, given a source pointer, item count, and item size.mem_alloc_clone— Allocate a block of memory and copy memory into it, given only a source pointer. The size of the block is automagically determined by callingmem_size.mem_dealloc— Deallocate a block of memory, given a pointer, and set that pointer to null. This is afreewrapper, implemented as a hidden function and a visible macro.mem_size— Return the size of a previously allocated block of memory. This is simply amalloc_sizewrapper.
Note that none of these take a size_t of bytes directly. Instead, they compute the size by multiplying a count of items times the size of an item (like calloc does). This is much more convenient for me and saves me a whole bunch of parenthesization in client code.
And now here is the source code...
Header file
extern size_t mem_size (void *memory);
extern void *mem_alloc (size_t item_count, size_t item_size);
extern void *mem_alloc_clear (size_t item_count, size_t item_size);
extern void mem_clear (void *memory, size_t item_count, size_t item_size);
extern void *mem_realloc (void *memory, size_t item_count, size_t item_size);
extern void *mem_alloc_copy (void *memory, size_t item_count, size_t item_size);
extern void *mem_alloc_clone (void *memory);
extern void mem_copy (void *memory_dest, void *memory_src, size_t item_count, size_t item_size);
extern void mem_dealloc_ (void **memory); // (Do not call directly)
#define mem_dealloc(p) mem_dealloc_((void **)(void *)(p))
Notes:
The actual deallocation routine, named
mem_dealloc_, should not be called directly, as it is wrapped by a macro namedmem_dealloc, which allows the caller to writemem_dealloc(&pointer)without the compiler warning about incompatible pointer types and without having to explicitly writemem_dealloc((void **)&pointer).The reason for writing
(void **)(void *)(p)instead of(void **)(p)in themem_deallocmacro is because the latter causes compile-time errors with ARC (Automatic Reference Counting) in cases of memory pointing to Objective-C objects, whereas the former has no such issue.
Implementation file
Return allocation size
mem_size returns the size in bytes of a previously-allocated memory block. It relies on the built-in function malloc_size.
size_t mem_size(void *memory)
{
if (memory != NULL)
return malloc_size(memory);
else
return 0;
}
Allocate memory
mem_alloc allocates a new block of memory, given an item count and item size. Upon failure, like malloc, it returns NULL.
void *mem_alloc(size_t item_count, size_t item_size)
{
void *memory = malloc(item_count * item_size);
return memory;
}
Allocate and clear memory
mem_alloc_clear allocates and clears a new block of memory (technically, it allocates a block of cleared memory), given an item count and item size. Upon failure, like calloc, it returns NULL.
void *mem_alloc_clear(size_t item_count, size_t item_size)
{
void *memory = calloc(item_count, item_size);
return memory;
}
Clear memory
mem_clear clears all bytes in a block of memory to zero, given a block pointer, an item count, and item size.
void mem_clear(void *memory, size_t item_count, size_t item_size)
{
assert(memory != NULL);
memset(memory, 0, item_count * item_size);
}
Copy memory
mem_copy copies a block of memory, given a source and destination pointer (in traditional C reverse order), an item count, and item size.
void mem_copy(void *memory_dest, void *memory_src, size_t item_count, size_t item_size)
{
assert(memory_dest != NULL);
assert(memory_src != NULL);
memcpy(memory_dest, memory_src, item_count * item_size);
}
Reallocate memory
mem_realloc reallocates a block of memory, given a pointer to the existing block and a new item count and item size. Upon failure, like realloc, it returns NULL.
void *mem_realloc(void *memory, size_t item_count, size_t item_size)
{
memory = realloc(memory, item_count * item_size);
return memory;
}
Allocate and copy memory
mem_alloc_copy allocates a new block of memory and copies the contents of an existing area of memory, given a pointer to the existing area, an item count, and item size. Upon failure, it returns NULL.
void *mem_alloc_copy(void *memory, size_t item_count, size_t item_size)
{
assert(memory != NULL);
void *new_memory = mem_alloc(item_count, item_size);
if (new_memory != NULL)
mem_copy(new_memory, memory, item_count, item_size);
return new_memory;
}
Allocate cloned memory
mem_alloc_clone allocates a new block of memory, cloning an existing block of memory into it. To determine the size of the block, it relies upon the mem_size function. Upon failure, it returns NULL.
void *mem_alloc_clone(void *memory)
{
assert(memory != NULL);
return mem_alloc_copy(memory, mem_size(memory), 1);
}
Deallocate memory
mem_dealloc_ deallocates a block of memory, given a pointer to it. This routine should not be called directly, but only via the mem_dealloc macro, which is defined in the header file.
mem_dealloc deallocates a block of memory, given a pointer to a pointer to it, and then sets the pointer to NULL.
The calling convention here is to write mem_dealloc(&pointer).
void mem_dealloc_(void **memory)
{
assert(memory != NULL);
if (*memory)
{
free(*memory);
*memory = NULL;
}
}
memmove()? \$\endgroup\$memmove(). I've never really needed it in 25 years of C programming, except maybe once long ago when shifting some strings around the hard way. \$\endgroup\$restrictin mem_copy. \$\endgroup\$