Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: In " If p were not a {{widget *, the compiler...," is "were not a" supposed to be part of the code sample?

...

The argument to malloc() can be any value of (unsigned) type size_t. If the program uses the allocated storage to represent an object (possibly an array) whose size is greater than the requested size, the behavior is undefined. The implicit pointer conversion lets this slip by without complaint from the compiler.

For example,

Code Block
bgColor#FFcccc
#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
bgColor#FFcccc
p = malloc(sizeof(gadget)); /* imminent problem */

...

Casting the result of malloc() to the appropriate pointer type enables the compiler to catch subsequent inadvertent pointer conversions. When allocating individual objects, the "appropriate pointer type" is a pointer to the type argument in the sizeof expression passed to malloc(), as in:

Code Block
bgColor#ccccff
widget *p;

/* ... */

p = (gadget *)malloc(sizeof(gadget)); /* invalid assignment */

...

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
bgColor#ccccff
#define MALLOC(type) ((type *)malloc(sizeof(type)))

further reduces the possibility of error.:

Code Block
bgColor#ccccff
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
bgColor#ccccff
#define MALLOC_ARRAY(number, type) \
    ((type *)malloc(number * sizeof(type)))

...

Code Block
bgColor#ccccff
/* 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)))

For example,

Code Block
bgColor#ccccff
enum month { Jan, Feb, ... };
type enum month month;

typedef enum 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);

...