Hacker News new | ask | show | jobs
by weinzierl 4045 days ago
In C there are no functions that take a type as argument.

So

   sizeof(type)
is a special case anyway.

I'm all for using sizeof like a function, but that doesn't make it consistent. sizeof is just a special syntactical construct.

I like to think that sizeof is called an operator just for syntactic convenience much in the same way as typedef is a storage-class specifier.

2 comments

And you really shouldn't use it with a type if you can avoid it anyway, it makes code brittle e.g.

    int *foo;
    // code
    foo = malloc(sizeof(int));
a few months later, change foo to be a double. Code still compiles, no warning, but you're allocating half the memory you need.
This is a really great example why you SHOULDN'T think of sizeof as a function. If sizeof were a function, the code

  int *foo = NULL;
  foo = malloc(sizeof(*foo));
would be undefined behavior (dereferencing NULL)!
Dereferencing a null pointer is legal in C. It's the conversion of a null pointer from from an r-value to an l-value that's illegal, which does not happen in that snippet of code.

That's why it's perfectly legal in C to do this (&*foo), even if foo is a null pointer.

> Dereferencing a null pointer is legal in C. It's the conversion of a null pointer from from an r-value to an l-value that's illegal, which does not happen in that snippet of code.

And it does not happen in that snippet of code because sizeof is nothing like a function.

Which is why it's nice to lift stuff out into typedefs. It centralizes them (DRY principle) and avoids this issue.

    typedef int thing_t;
    ...
    thing_t *foo;
    // code
    foo = malloc(sizeof(thing_t));
Why is that better than just getting `sizeof(* foo)`? Even with typedef, I can see this happening in the future:

    typedef int thing_t;
    ...
    thing_t *foo_internal;
    thing_wrapper_t *foo;
    // code
    foo = malloc(sizeof(thing_t));
If you do:

    foo = malloc(sizeof(*foo));
That's at least always on the same line.
That just makes it more verbose.

This, on the other hand, always allocates one object of foo's pointed-to-size, whatever its type:

    foo = malloc(sizeof(*foo));
As an aside, I think nearly any time you want a typedef, it's worth wrapping it in a struct.

    typedef struct { int value; } thing_t;
That way the compiler catches it when you try to pass the wrong thing (at least, more of the time).
DRY is good, but making the structure of your code reflect the actual semantics you want is better. What you want is to allocate space for foo. So write that.
Well, in this (simple) case you can just do

    int *foo;
    foo = malloc(sizeof(*foo));
And avoid the brittleness mentioned.
There are standard macros (va_arg, I'm looking at you) that take a type as an argument.