Merge malloc/calloc/realloc/free into one safe macro, and test code
I standalize actions of checking NULL before allocation, setting pointer to NULL after released, and initializing newly allocated memory to 0. Only flaw is, pointer must be manually initized to NULL on declaraction, which cannot be gracefully implemented by macro. #include #include #include #include #define log_error(__arg_format, ...) \ printf("ERROR %s:%d:%s: " __arg_format "\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__) #define fatal(__arg_format, ...) \ do { \ log_error("Fatal error: " __arg_format, ##__VA_ARGS__); \ abort(); \ } while (0) #define enforce(__arg_condition) \ do { \ if (!(__arg_condition)) { \ fatal("Enforcement failed: %s", #__arg_condition); \ } \ } while (0) #if defined(_WIN32) #include #define allocated_size _msize #elif defined(__linux__) #include #define allocated_size malloc_usable_size #elif defined(__APPLE__) #include #define allocated_size malloc_size #else #error Looking up allocated memory block size is not supported in this platform. #endif #define allocate(__arg_pointer, __arg_unit_count) \ do { \ typeof(__arg_unit_count) __unit_count = __arg_unit_count; \ size_t __size_per_unit = sizeof(typeof(*__arg_pointer)); \ size_t __size = __unit_count * __size_per_unit; \ if (__arg_pointer == NULL) { \ if (__unit_count > 0) { \ __arg_pointer = (typeof(__arg_pointer))calloc(__unit_count, __size_per_unit); \ enforce(__arg_pointer != NULL); \ } \ } else { \ if (__unit_count > 0) { \ size_t __old_size = allocated_size(__arg_pointer); \ __arg_pointer = (typeof(__arg_pointer))realloc(__arg_pointer, __size); \ enforce(__arg_pointer != NULL); \ if (__size > __old_size) { \ memset((char *)(__arg_pointer) + __old_size, 0, __size - __old_size); \ } \ } else { \ free(__arg_pointer); \ __arg_pointer = NULL; \ } \ } \ } while (0) struct foo { char a; short b; long c; long long d; }; // msvc compile options: cl /W3 /utf-8 /std:clatest // open task manager to check whether memory leaks int main() { struct foo *foo = NULL; struct foo *ptr; int old_count = 0; int new_count = 0; int offset; srand((unsigned)time(NULL)); for (;;) { // check whether old memory is 0xff for (offset = 0; offset a == -1); enforce(ptr->b == -1); enforce(ptr->c == -1); enforce(ptr->d == -1); } // set a smaller value to make more 0 appear for testing 'free()' new_count = rand() % 10; allocate(foo, new_count); // check whether new space is 0 for (offset = old_count; offset a == 0); enforce(ptr->b == 0); enforce(ptr->c == 0); enforce(ptr->d == 0); } // fill 0xff for (offset = 0; offset a = -1; ptr->b = -1; ptr->c = -1; ptr->d = -1; } old_count = new_count; } return EXIT_SUCCESS; }

I standalize actions of checking NULL before allocation, setting pointer to NULL after released, and initializing newly allocated memory to 0. Only flaw is, pointer must be manually initized to NULL on declaraction, which cannot be gracefully implemented by macro.
#include
#include
#include
#include
#define log_error(__arg_format, ...) \
printf("ERROR %s:%d:%s: " __arg_format "\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__)
#define fatal(__arg_format, ...) \
do { \
log_error("Fatal error: " __arg_format, ##__VA_ARGS__); \
abort(); \
} while (0)
#define enforce(__arg_condition) \
do { \
if (!(__arg_condition)) { \
fatal("Enforcement failed: %s", #__arg_condition); \
} \
} while (0)
#if defined(_WIN32)
#include
#define allocated_size _msize
#elif defined(__linux__)
#include
#define allocated_size malloc_usable_size
#elif defined(__APPLE__)
#include
#define allocated_size malloc_size
#else
#error Looking up allocated memory block size is not supported in this platform.
#endif
#define allocate(__arg_pointer, __arg_unit_count) \
do { \
typeof(__arg_unit_count) __unit_count = __arg_unit_count; \
size_t __size_per_unit = sizeof(typeof(*__arg_pointer)); \
size_t __size = __unit_count * __size_per_unit; \
if (__arg_pointer == NULL) { \
if (__unit_count > 0) { \
__arg_pointer = (typeof(__arg_pointer))calloc(__unit_count, __size_per_unit); \
enforce(__arg_pointer != NULL); \
} \
} else { \
if (__unit_count > 0) { \
size_t __old_size = allocated_size(__arg_pointer); \
__arg_pointer = (typeof(__arg_pointer))realloc(__arg_pointer, __size); \
enforce(__arg_pointer != NULL); \
if (__size > __old_size) { \
memset((char *)(__arg_pointer) + __old_size, 0, __size - __old_size); \
} \
} else { \
free(__arg_pointer); \
__arg_pointer = NULL; \
} \
} \
} while (0)
struct foo {
char a;
short b;
long c;
long long d;
};
// msvc compile options: cl /W3 /utf-8 /std:clatest
// open task manager to check whether memory leaks
int main() {
struct foo *foo = NULL;
struct foo *ptr;
int old_count = 0;
int new_count = 0;
int offset;
srand((unsigned)time(NULL));
for (;;) {
// check whether old memory is 0xff
for (offset = 0; offset < old_count; offset++) {
ptr = foo + offset;
enforce(ptr->a == -1);
enforce(ptr->b == -1);
enforce(ptr->c == -1);
enforce(ptr->d == -1);
}
// set a smaller value to make more 0 appear for testing 'free()'
new_count = rand() % 10;
allocate(foo, new_count);
// check whether new space is 0
for (offset = old_count; offset < (new_count - old_count); offset++) {
ptr = foo + offset;
enforce(ptr->a == 0);
enforce(ptr->b == 0);
enforce(ptr->c == 0);
enforce(ptr->d == 0);
}
// fill 0xff
for (offset = 0; offset < new_count; offset++) {
ptr = foo + offset;
ptr->a = -1;
ptr->b = -1;
ptr->c = -1;
ptr->d = -1;
}
old_count = new_count;
}
return EXIT_SUCCESS;
}