Instead of using sizeof(type), use sizeof *p,is it safe and correct?

Viewed 442

Is this safe to use,This code compiled with gcc 4.9.2 without any error or warning

widget *p;
...
p = malloc(sizeof *p);

I found this on SEI CERT C Coding Standard website.

Click here -- no type mismatch issues, no need for casting. You allocate the right amount of memory every time.


struct widget;
typedef struct widget widget_t;

struct gadget;
typedef struct gadget gadget_t;

widget_t *newWidget(void)
{
    widget_t *p = malloc(sizeof *p);
    if (p) 
        /* initialize members of *p as necessary */
    return p;
} 

gadget_t *newGadget(void)
{
    gadget_t *p = malloc(sizeof *p);
    if (p)
        /* initialize members of *p as necessary */
    return p;
}

void deleteWidget(widget_t **p)
{
     /* delete any subelements of *p */
     free(*p);
     *p = NULL;
}

void deleteGadget(gadget_t **p)
{
    /* delete any subelements of *p */
    free(*p);
    *p = NULL;
}

...

widget_t *p = newWidget();
gadget_t *g = newGadget();

if (p)
    /* do stuff with p */

if (g)
    /* do stuff with g */
...
deleteWidget(&p); 
deleteGadget(&g); 

4 Answers

It's a good coding practice!

Imagine this code

struct structv1 *p1 = malloc(sizeof (struct structv1));
struct structv1 *p2 = malloc(sizeof *p2);

gets changed to

struct structv2 *p1 = malloc(sizeof (struct structv2));
//     ^^^^^^^^                     ^^^^^^^^^^^^^^^^^
// two changes! maybe the programmer forgets one of them
struct structv2 *p2 = malloc(sizeof *p2);
//     ^^^^^^^^
// one change only. the argument to malloc is already correct

The sizeof operator has the following definition

sizeof unary-expression
sizeof ( type-name )

Thus in this declaration

widget_t *p = malloc(sizeof *p);

there is used the first form of the operator where the expression *p is an unary expression and has the type of widget_t.

Thus these declarations

widget_t *p = malloc(sizeof *p);
widget_t *p = malloc(sizeof( widget_t ) );

are totally equivalent.

The first declaration is preferable because the expression in the sizeof operator does not depend on the actual type. That is the type of the pointer can be changed but the declaration will be valid without any other changes.

In C there is no need to cast the pointer returned from malloc to the type of the assigned lvalue because a pointer of the type void * may be assigned to pointer to object of any type. It is sometimes used (and moreover sometimes useful) to make the program self-documented.

Short answer: yes, it's safe.

sizeof isn't a function; it's an operator. Used as you've shown, it returns the size in bytes of the object representation of the type of the expression. The expression itself isn't evaluated at run-time; it instead feeds a type to the sizeof operator at compile time, and thus no harm or foul.

Using sizeof *p instead of sizeof (type) is safe with one exception - if p is an uninitialized or invalid pointer to a variable-length array, then the behavior is undefined. For example:

size_t rows, cols;
...
T (*p)[cols] = malloc( rows * sizeof *p );  // *p is undefined here

For every type except variably-modified types, the operand of sizeof is not evaluated. For variable-length arrays, it is evaluated, and since p is invalid until after the malloc call completes, applying the * operator to it results in undefined behavior.

Chapter and verse

6.5.3.2 Address and indirection operators
...
4 The unary * operator denotes indirection. If the operand points to a function, the result is a function designator; if it points to an object, the result is an lvalue designating the object. If the operand has type ‘‘pointer to type’’, the result has type ‘‘type’’. If an invalid value has been assigned to the pointer, the behavior of the unary * operator is undefined.102)
...
6.5.3.4 The sizeof and _Alignof operators
...
2 The sizeof operator yields the size (in bytes) of its operand, which may be an expression or the parenthesized name of a type. The size is determined from the type of the operand. The result is an integer. If the type of the operand is a variable length array type, the operand is evaluated; otherwise, the operand is not evaluated and the result is an integer constant.
102) ... Among the invalid values for dereferencing a pointer by the unary * operator are a null pointer, an address inappropriately aligned for the type of object pointed to, and the address of an object after the end of its lifetime.

Now, I've used the above code a number of times and have never had any issues, but that's not a guarantee of anything - I've only ever used it on one particular architecture (x68) and with a limited range of compilers. There's no guarantee that it won't blow up spectacularly on some oddball architecture.

It's an incredibly useful idiom, though, and I kind of wish the standard were worded better to properly define it. I don't have a copy of the very latest (2011 is the most recent I have access to), so I don't know if they've tweaked that language any.

Related