I have a question about some code in Eric Roberts' Programming Abstractions in C. He use several libraries of his own both to simplify things for readers and to teach how to write libraries. (All of the library code for the book can be found on this site.)
One library, genlib
provides a macro for generic allocation of a pointer to a struct
type. I don't understand part of the macro. I'll copy the code below, plus an example of how it is meant to be used, then I'll explain my question in more detail.
/*
* Macro: New
* Usage: p = New(pointer-type);
* -----------------------------
* The New pseudofunction allocates enough space to hold an
* object of the type to which pointer-type points and returns
* a pointer to the newly allocated pointer. Note that
* "New" is different from the "new" operator used in C++;
* the former takes a pointer type and the latter takes the
* target type.
*/
#define New(type) ((type) GetBlock(sizeof *((type) NULL)))
/* GetBlock is a wrapper for malloc. It encasulates the
* common sequence of malloc, check for NULL, return or
* error out, depending on the NULL check. I'm not going
* to copy that code since I'm pretty sure it isn't
* relevant to my question. It can be found here though:
* ftp://ftp.awl.com/cseng/authors/roberts/cs1-c/standard/genlib.c
*/
Roberts intends for the code to be used as follows:
typedef struct {
string name;
/* etc. */
} *employeeT;
employeeT emp;
emp = New(employeeT);
He prefers to use a pointer to the record as the type name, rather than the record itself. So New
provides a generic way to allocate such struct
records.
In the macro New
, what I don't understand is this: sizeof *((type)) NULL)
. If I'm reading that correctly, it says "take the size of the dereferenced cast of NULL
to whatever struct
type type
represents in a given call". I think I understand the dereferencing: we want to allocate enough space for the struct; the size of the pointer is not what we need, so we dereference to get at the size of the underlying record-type. But I don't understand the idea of casting NULL
to a type.
My questions:
- You can cast
NULL
? What does that even mean? Why is the cast necessary? When I tried removing it, the compiler says
error: expected expression
. So,sizeof *(type)
is not an expression? That confused me since I can do the following to get the sizes of arbitrary pointers-to-structs:#define struct_size(s_ptr) do { \ printf("sizeof dereferenced pointer to struct %s: %lu\n", \ #s_ptr, sizeof *(s_ptr)); \ } while(0)
Edit: As many people point out below, the two examples aren't the same:
/* How genlib uses the macro. */
New(struct MyStruct*)
/* How I was using my macro. */
struct MyStruct *ptr; New(ptr)
For the record, this isn't homework. I'm an amateur trying to improve at C. Also, there's no problem with the code, as far as I can tell. That is, I'm not asking how I can do something different with it. I'm just trying to better understand (1) how it works and (2) why it must be written the way it is. Thanks.
struct_size
macro is invalid ifs_ptr
isn't an expression. If you passstruct MyStruct *
ass_ptr
, the code won't compile, sincesizeof *(struct MyStruct *)
is not valid C... And no,*(type)
isn't an expression because it wouldn't make sense - what exactly did you expect*(type)
to evaluate to? – Laruelarum