...
Consider the following example:
Code Block |
---|
|
#include <stdlib.h>
typedef struct gadget gadget;
struct gadget {
int i;
double d;
};
typedef struct widget widget;
struct widget {
char c[10];
int i;
double d;
};
widget *p;
/* ... */
p = malloc(sizeof(gadget)); /* imminent problem */
if (p != NULL) {
p->i = 0; /* undefined behavior */
p->d = 0.0; /* undefined behavior */
}
|
An implementation may add padding to a gadget or widget so that sizeof(gadget)
equals sizeof(widget)
, but this is highly unlikely. More likely, sizeof(gadget)
is less than sizeof(widget)
. In that case
Code Block |
---|
|
p = malloc(sizeof(gadget)); /* imminent problem */
|
...
In this code example, malloc()
allocates space for a gadget
and the cast immediately converts the returned pointer to a gadget *
:
Code Block |
---|
|
widget *p;
/* ... */
p = (gadget *)malloc(sizeof(gadget)); /* invalid assignment */
|
...
This compliant solution repeats the same type in the sizeof
expression and the pointer cast.
Code Block |
---|
|
widget *p;
/* ... */
p = (widget *)malloc(sizeof(widget));
|
...
Repeating the same type in the sizeof
expression and the pointer cast is easy to do but still invites errors. Packaging the repetition in a macro, such as
Code Block |
---|
|
#define MALLOC(type) ((type *)malloc(sizeof(type)))
|
further reduces the possibility of error.
Code Block |
---|
|
widget *p;
/* ... */
p = MALLOC(widget); /* OK */
if (p != NULL) {
p->i = 0; /* OK */
p->d = 0.0; /* OK */
}
|
...
When allocating an array with N
elements of type T
, the appropriate type in the cast expression is still T *
, but the argument to malloc()
should be of the form N * sizeof(T)
. Again, packaging this form as a macro, such as
Code Block |
---|
|
#define MALLOC_ARRAY(number, type) \
((type *)malloc((number) * sizeof(type)))
|
reduces the chance of error in an allocation expression.
Code Block |
---|
|
enum { N = 16 };
widget *p;
/* ... */
p = MALLOC_ARRAY(N, widget); /* OK */
|
A small collection of macros can provide secure implementations for common uses for the standard memory allocation functions. The omission of a REALLOC()
macro is intentional. (See recommendation MEM08-C. Use realloc() only to resize dynamically allocated arrays.)
Code Block |
---|
|
/* allocates a single object using malloc(). */
#define MALLOC(type) ((type *)malloc(sizeof(type)))
/* allocates an array of objects using malloc(). */
#define MALLOC_ARRAY(number, type) \
((type *)malloc((number) * sizeof(type)))
/* allocates a single object with a flexible
* array member using malloc(). */
#define MALLOC_FLEX(stype, number, etype) \
((stype *)malloc(sizeof(stype) \
+ (number) * sizeof(etype)))
/* allocates an array of objects using calloc(). */
#define CALLOC(number, type) \
((type *)calloc(number, sizeof(type)))
/* reallocates an array of objects using realloc(). */
#define REALLOC_ARRAY(pointer, number, type) \
((type *)realloc(pointer, (number) * sizeof(type)))
/* reallocates a single object with a flexible
* array member using realloc(). */
#define REALLOC_FLEX(pointer, stype, number, etype) \
((stype *)realloc(pointer, sizeof(stype) \
+ (number) * sizeof(etype)))
|
The following is an example:
Code Block |
---|
|
enum month { Jan, Feb, /* ... */ };
typedef enum month month;
typedef struct date date;
struct date {
unsigned char dd;
month mm;
unsigned yy;
};
typedef struct string string;
struct string {
size_t length;
char text[];
};
date *d, *week, *fortnight;
string *name;
d = MALLOC(date);
week = MALLOC_ARRAY(7, date);
name = MALLOC_FLEX(string, 16, char);
fortnight = CALLOC(14, date);
|
...