Possible Duplicates:
Does the 'offsetof' macro from <stddef.h> invoke undefined behaviour?
dereferencing the null pointer
#define _OFFS_OF_MEMBER(p_type, p_member) (size_t)&(((p_type *)NULL)->p_member)
struct a
{
int a, b;
};
size_t l = _OFFS_OF_MEMBER(struct a, b);
I had a little chat/conversation with some fellow users, and one of them said that this is dereferencing and accessing the address space near address NULL. I said: taking an address of a member will not access, touch, or read the value of that member. According to standard it is completely safe.
struct a* p = NULL;
size_t offset = &p->b; // this may NOT touch b, it is not dereferencing
// p->b = 0; // now, we are dereferincing: acccess violation time!
Is this always a safe way to calculate offset, or are compilers free to dereference and mess up the memory near address NULL according to standards?
I know there is a safe way to calculate offsets provided by the standard, but I am curious what you have to say about this. All in favor of my explenation: up-vote this question :-)
You're not dereferencing anything invalid here. All that macro does is tell the compiler that a structure of type p_type
exists in memory at the address NULL
. It then takes the address of p_member
, which is a member of this fictitious structure. So, no dereferencing anywhere.
In fact, this is exactly what the offsetof
macro, defined in stddef.h
does.
EDIT:
As some of the comments say, this may not work well with C++ and inheritance, I've only used offsetof
with POD structures in C.
Absolutely not. To even create a pointer by adding an offset to NULL is to invoke Undefined Behavior. Someone more motivated can dig up chapter and verse from the spec.
By the way, whatever your reason is for wanting to compute these offsets, it is a probably a bad one.
It is invalid C++.
From ISO/IEC 14882:2003, 5.2.5:
3/ If E1 has the type “pointer to class X,” then the expression E1->E2
is converted to the equivalent form (*(E1)).E2 (...)
However, there has been a defect report about this, and it is valid C99 (and probably valid C++0x too):
From ISO/IEC 9899:1999, 6.5.3:
2/ If the operand [of the unary & operator] is the result of a unary *
operator, neither that operator nor the & operator is evaluated and
the result is as if both were omitted, except that the constraints on
the operators still apply and the result is not an lvalue.
So &p->b
is &(p->b)
is (by definition) &((*p).b)
, which does seem to involve a dereference of p
before getting the member. It may work on most compilers though even if it violates the standard. As noted in a comment this work probably work right in cases involving multiple inheritance.
What problem are you trying to solve by getting this offset? Could you use references, pointers, or pointers-to-member instead?
#define _OFFS_OF_MEMBER(p_type, p_member) (size_t)&(((p_type *)NULL)->p_member)
struct a
{
int a, b;
};
size_t l = _OFFS_OF_MEMBER(struct a, b);
Is the same as (after preprocessing)
struct a { int a, b; };
size_t l = (size_t)&(((struct a *)NULL)->b);
I see that you are casting NULL to a pointer-to-struct a, and then getting the address of its member b.
As far as I know, since you are getting the address of b, and not actually accessing or modifying (dereferencing) the value of b, the compiler will not complain, and you will not get a runtime error. Since NULL (or 0) is the starting address of a, this will give you the offset. This is actually a pretty nifty way to do that.