Puzzling #define

  • Thread starter Edward Rutherford
  • Start date
E

Edward Rutherford

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.
 
E

Edward A. Falk

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.
 
J

Joel C. Salomon

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.

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
 
S

Shao Miller

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...
 
S

Shao Miller

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].
 
J

Joel C. Salomon

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
 
T

Tim Rentsch

[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 )
 
M

Morris Keesan

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.
 
S

Shao Miller

[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 )

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

Shao Miller

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...

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.
 
S

Shao Miller

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...

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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,952
Messages
2,570,111
Members
46,692
Latest member
NewtonChri

Latest Threads

Top