Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Puzzling #define

15 views
Skip to first unread message

Edward Rutherford

unread,
Jun 2, 2011, 5:41:00 PM6/2/11
to
I'm having trouble grasping the meaning behind the following:

#define MAKE(z, w) (int)(&(((z *)0)->w))

Can anyone fill me in?

Specifically, I'm confused on the ((z *)0) part. I'm not exactly sure
what that would give me.

T. I. A.

christian.bau

unread,
Jun 2, 2011, 5:45:55 PM6/2/11
to
On Jun 2, 10:41 pm, Edward Rutherford

Looks like a horrible attempt to create a macro that does the same
thing as offsetof, except that this one invokes undefined behaviour.

Assuming that the parameter z in the macro is some type, most likely a
struct type, (z *)0 takes the number 0, and converts it to a pointer
to an object of type z. In other words, a null pointer of type z*.

Edward A. Falk

unread,
Jun 2, 2011, 6:09:46 PM6/2/11
to
In article <53dbaa2b-5fe0-4ce3...@u19g2000vbi.googlegroups.com>,

christian.bau <christ...@cbau.wanadoo.co.uk> wrote:
>On Jun 2, 10:41 pm, Edward Rutherford
><edward.p.rutherfor...@REMOVETHIS.gmail.com> wrote:
>> I'm having trouble grasping the meaning behind the following:
>>
>> #define MAKE(z, w) (int)(&(((z *)0)->w))
>>
>> Can anyone fill me in?
>>
>> Specifically, I'm confused on the ((z *)0) part.  I'm not exactly sure
>> what that would give me.

Taken as a whole, the macro returns the byte offset of element 'w'
within an object of type 'z'. Why they chose to name it MAKE is
a mystery to me.

Expanding: ((z *)0) is zero, cast as a pointer to z. &(((z *)0)->w)
is the address of element 'w' within a 'z' object. Because this
particular 'z' object starts at zero, this pointer is also the
byte address of 'w'. The (int) cast causes the whole macro to return
an integer value of the byte address.

This macro makes some assumptions about the underlying CPU architecture.
If you look up offsetof() in Wikipedia, you'll find a somewhat more
portable definition of offsetof() along with a discussion about
the undefined behavior that christian.bau mentioned.

--
-Ed Falk, fa...@despams.r.us.com
http://thespamdiaries.blogspot.com/

Joel C. Salomon

unread,
Jun 2, 2011, 6:21:36 PM6/2/11
to

The expression `(z *)0` is a null pointer of type `z*`. This isn't
quite the same as a "zero-valued" pointer (e.g., a null pointer may have
some arbitrary implementation-specific bit pattern that serves as a
trap), but the writer makes the assumption that null = zero.

Assuming for the moment that `(z *)0` gives you a zero-valued pointer,
the expression `((z *)0)->w` pretends there is a structure of type `z`
at address 0, and accesses the `w` field of the structure. The address
of this field is then taken, and the resulting address is cast to `int`.

E.g., given the structure definition
struct foo{
int32_t bar;
int32_t bas;
};
and plausible assumptions about structure layout and byte size,
`MAKE(foo foo, bas)` is intended to yield 4.

(Oh, and if differently-typed pointers have different representations on
your system, this will break horribly in very interesting ways.)

See also the `offsetof()` macro in <stddef.h>, which accomplishes this
same thing, but is defined by your compiler provider in a way which is
guaranteed to work on your machine.


By the way: Is there a portable way to get a zero-valued pointer? To
take a concrete example, assume a system where uintptr_t is defined, and
on which
union intptr {
void *p;
uintptr_t i;
};
/* ... */
union intptr u = {.p = 0};
assert(u.i == 0xDEADBEEF);
holds. On this system, is there an initializer for a pointer value for
which
union intptr v = {.p = ZERO_PTR};
assert(u.i == 0);
holds?

This should work:
#define ZERO_PTR ((intptr){.i = 0}).p
but is there a way to not require the union definition?

I'm wondering if `(void *)((uintptr_t)0)` might do the trick -- or is
the void* <-> uintptr_t conversion defined to map null to zero?

--Joel

Shao Miller

unread,
Jun 2, 2011, 8:33:46 PM6/2/11
to
On 6/2/2011 5:09 PM, Edward A. Falk wrote:
> This macro makes some assumptions about the underlying CPU architecture.
> If you look up offsetof() in Wikipedia, you'll find a somewhat more
> portable definition of offsetof() along with a discussion about
> the undefined behavior that christian.bau mentioned.
>

Speaking of which, in:

#define fun_offsetof(type, member) \
(sizeof (char[(char *)&((type *)0)->member - (char *)0]))

struct s {
int x;
short y;
int z;
};

int main(void) {
return (int)fun_offsetof(struct s, z);
}

I wonder if that's any more portable...

Shao Miller

unread,
Jun 2, 2011, 8:47:25 PM6/2/11
to
On 6/2/2011 5:21 PM, Joel C. Salomon wrote:
> On 06/02/2011 05:41 PM, Edward Rutherford wrote:
>> I'm having trouble grasping the meaning behind the following:
>>
>> #define MAKE(z, w) (int)(&(((z *)0)->w))
>>
> Assuming for the moment that `(z *)0` gives you a zero-valued pointer,
> the expression `((z *)0)->w` pretends there is a structure of type `z`
> at address 0, and accesses the `w` field of the structure. The address
> of this field is then taken, and the resulting address is cast to `int`.
>

Are you sure about "accesses the `w` field"? 6.3.2.1p2 suggests to me
that there is no access[3.1p1].

Joel C. Salomon

unread,
Jun 2, 2011, 8:49:01 PM6/2/11
to
On 06/02/2011 08:47 PM, Shao Miller wrote:
> On 6/2/2011 5:21 PM, Joel C. Salomon wrote:
>> Assuming for the moment that `(z *)0` gives you a zero-valued pointer,
>> the expression `((z *)0)->w` pretends there is a structure of type `z`
>> at address 0, and accesses the `w` field of the structure.
>
> Are you sure about "accesses the `w` field"? 6.3.2.1p2 suggests to me
> that there is no access[3.1p1].

You're right, of course; an *actual* access would be a Bad Thing. I was
using the word "access" somewhat loosely. (Also, if I read this right,
offsetof() is supposed to work at compile-time.)

Good catch.

--Joel

Tim Rentsch

unread,
Jun 2, 2011, 9:39:17 PM6/2/11
to
"Joel C. Salomon" <joelcs...@gmail.com> writes:

[snip]


>
> By the way: Is there a portable way to get a zero-valued pointer? To
> take a concrete example, assume a system where uintptr_t is defined, and
> on which
> union intptr {
> void *p;
> uintptr_t i;
> };
> /* ... */
> union intptr u = {.p = 0};
> assert(u.i == 0xDEADBEEF);
> holds. On this system, is there an initializer for a pointer value for
> which
> union intptr v = {.p = ZERO_PTR};
> assert(u.i == 0);
> holds?
>
> This should work:
> #define ZERO_PTR ((intptr){.i = 0}).p
> but is there a way to not require the union definition?

How about this:

#define ZERO_POINTER ALL_ZEROES( void * )

#define ALL_ZEROES(T) \
( ( (union { unsigned char uc[sizeof(T)]; T it; }){{0}} ).it )

Shao Miller

unread,
Jun 2, 2011, 10:55:36 PM6/2/11
to

Agreed. :) I use that pattern, too, in C99.

Morris Keesan

unread,
Jun 2, 2011, 10:34:21 PM6/2/11
to
On Thu, 02 Jun 2011 18:21:36 -0400, Joel C. Salomon
<joelcs...@gmail.com> wrote:

> By the way: Is there a portable way to get a zero-valued pointer?

Assuming that by "zero-valued pointer", you mean a pointer all of whose
bits are zero,

#include <string.h>
void *zerop;
memset(&zerop, 0, sizeof zerop);

causes zerop to be a "zero-valued" (void *). I don't think you can
portably do anything with it, because it could then contain a trap
representation. Even using it as an initializer for other pointers
could be a problem.
--
Morris Keesan -- mke...@post.harvard.edu

Shao Miller

unread,
Jun 2, 2011, 11:37:17 PM6/2/11
to

Or perhaps the more ludicrous:

#define DUMMY_FUNC(type) \
((type * (*)(void))main)

#define NULLOF(type) (1 ? 0 : DUMMY_FUNC(type)())

#define fun_offsetof(type, member) \
(sizeof (char[ \

(char *)&(NULLOF(type)->member) - \
(char *)NULLOF(type) \
]))

struct s {
int x;
short y;
int z;
};

int main(void) {
return (int)fun_offsetof(struct s, z);
}

The "hope" being (for both examples) that 'sizeof' "helps" to avoid
evaluating the '->' operator's expression when its left operand is a
null pointer. The silly example above produces a null pointer without a
cast (for what that's worth, heh) and the ternary conditional operator
prevents 'main' from ever being called as the wrong function type. Just
for fun.

christian.bau

unread,
Jun 3, 2011, 10:36:28 AM6/3/11
to
On Jun 3, 1:33 am, Shao Miller <sha0.mil...@gmail.com> wrote:

> I wonder if that's any more portable...

There is "non-portable", and there is "undefined behaviour".

If we have say a valid pointer char* p, and if T is an integer type
large enough so that ((T) p) converts p to an integer type without
losing any bits, then it is non-portable to assume that (T) (p + 1) is
one larger than (T) p. For example, in DOS with "huge" pointers, that
difference could be let's say "interesting" (p + 1 could be in a
different segment than p, so the result could be say 0x1234ffff and
0x123c0000). In a segmented architecture, casting to an integer type
could put the segment part in the lower bits, and the offset in the
higher bits. So this macro could produce completely meaningless
results.

In MacOS X with 64 bit code, pointers are 64 bit with the first 4 GB
of address space unused, and int is 32 bit. As a result, casting any
valid pointer other than a null pointer to int is _guaranteed_ to lose
vital information. Therefore, a compiler might decide that the result
of (int) p is _always_ zero, so that macro would always produce
zero.

Pointer difference is undefined behavior when one pointer is a null
pointer. But you could do

void* p = malloc (sizeof (type));
size_t offset = (char *)&((type *) p)->member - (char *) p;
free (p);

which will calculate offset correctly unless p == NULL. And an
optimising compiler might optimise malloc and free away and just
assign the correct constant to offset.

Well, your macro invokes undefined behaviour and therefore anything
can happen (I think it is C99 only). The original macro invokes
undefined behaviour as well, but can produce meaningless even ignoring
that, as part of the normal workings of the implementation.

Shao Miller

unread,
Jun 3, 2011, 11:33:13 AM6/3/11
to
On 6/3/2011 10:36, christian.bau wrote:

> On 6/2/2011 23:37, Shao Miller wrote:
>> On 6/2/2011 7:33 PM, Shao Miller wrote:
>>> I wonder if that's any more portable...
>>
>> Or perhaps the more ludicrous:
>>
>> #define DUMMY_FUNC(type) \
>> ((type * (*)(void))main)
>>
>> #define NULLOF(type) (1 ? 0 : DUMMY_FUNC(type)())
>>
>> #define fun_offsetof(type, member) \
>> (sizeof (char[ \
>> (char *)&(NULLOF(type)->member) - \
>> (char *)NULLOF(type) \
>> ]))
>>
>> struct s {
>> int x;
>> short y;
>> int z;
>> };
>>
>> int main(void) {
>> return (int)fun_offsetof(struct s, z);
>> }
>>
>> The "hope" being (for both examples) that 'sizeof' "helps" to avoid evaluating the '->' operator's expression when its left operand is a null pointer. The silly example above produces a null pointer without a cast (for what that's worth, heh) and the ternary conditional operator prevents 'main' from ever being called as the wrong function type. Just for fun.
>
> There is "non-portable", and there is "undefined behaviour".
>
> If we have say a valid pointer char* p, and if T is an integer type
> large enough so that ((T) p) converts p to an integer type without
> losing any bits, then it is non-portable to assume that (T) (p + 1) is
> one larger than (T) p. For example, in DOS with "huge" pointers, that
> difference could be let's say "interesting" (p + 1 could be in a
> different segment than p, so the result could be say 0x1234ffff and
> 0x123c0000). In a segmented architecture, casting to an integer type
> could put the segment part in the lower bits, and the offset in the
> higher bits. So this macro could produce completely meaningless
> results.

Who's casting an integer to a pointer? Do you mean the code above? If
so, where? Do you mean the well-defined '(type *)0' in the first code?
I like to pretend that the representation of a pointer is
cryptographically signed and ought not to be tampered with.

>
> In MacOS X with 64 bit code, pointers are 64 bit with the first 4 GB
> of address space unused, and int is 32 bit. As a result, casting any
> valid pointer other than a null pointer to int is _guaranteed_ to lose
> vital information. Therefore, a compiler might decide that the result
> of (int) p is _always_ zero, so that macro would always produce
> zero.
>

Yeahbut who's doing that?

> Pointer difference is undefined behavior when one pointer is a null
> pointer. But you could do
>
> void* p = malloc (sizeof (type));
> size_t offset = (char *)&((type *) p)->member - (char *) p;
> free (p);
>

Even if the expression is not evaluated (in the code above)? Or are you
suggesting that the expression _is_ evaluated?

> which will calculate offset correctly unless p == NULL. And an
> optimising compiler might optimise malloc and free away and just
> assign the correct constant to offset.
>

That'd be great.

> Well, your macro invokes undefined behaviour and therefore anything
> can happen (I think it is C99 only). The original macro invokes
> undefined behaviour as well, but can produce meaningless even ignoring
> that, as part of the normal workings of the implementation.

Where does it invoke undefined behaviour, exactly? And no, it's
intended for C89. My news client might have mucked up indentation and
made it difficult to read. I apologize, but please do have a second
read of it.

0 new messages