0

I have inherited a lot of code and I am stumbling on this expansion. I understand what it does, I don't understand how it does it.

#define OFFSETOF(structure, item)     ((u16)&(((structure*)0)->item))

To be more specific, I get most of it except this part ((structure*)0). I don't get what the zero is doing or what it is there for.

If you need to know, 'structure' is just a typedef struct{} and item is a member in the

 struct{
    u32 my_example_item;
}

Again, the zero is confusing the heck out of me.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • One explanation for the macro is that the author doesn't know that the standard C language provides an `offsetof` macro in the standard header ``. – Kaz Jun 13 '14 at 18:33
  • The question actually isn't a duplicate of that one, because it is specifically asking about the null pointer constant. I prepared an answer to the question what the zero is doing there, but was not able to save. Please ask a more specific question, just about that expression, without the "red herring" material about the OFFSETOF macro. – Kaz Jun 13 '14 at 18:53

2 Answers2

2
  • structure is a type
  • (structure*) is a pointer
  • (structure*)0 is a null pointer of type structure*
  • ->item gets the item member of the object pointed at
  • (((structure*)0)->item) pretends there's a structure at position 0 in memory, and refers to it's item.
  • &(((structure*)0)->item) gets the address of that item, which will be equal to the offset of item in the object. The type of this address is u32* (since item is a u32)
  • ((u16)&(((structure*)0)->item)) converts that address to a u16, so it's a proper offset type.
Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
1

This is similar to a common implementation of the offsetof() macro, defined in the C standard header <stddef.h>. The code actually has undefined behavior, but it's likely to work for most C implementations.

It yields the offset, in bytes, of item from the beginning of type structure. structure should be a struct type (it could be a union, but then OFFSETOF will always yield 0), and item is a member of that structure (or union) type.

(structure*)0 is a null pointer of type pointer to structure.

((structure*)0)->item refers to the item member of the nonexistent structure object at the address that a null pointer points to. Yes, that's odd. If a null pointer is represented as memory address 0, and item is at an offset of, say, 12 bytes, then the object referred to by ((structure*)0)->item is at memory address 12. This makes a lot of assumptions about the memory model of the current system. Those assumptions are commonly valid; if they're not, this thing will probably blow up in your face.

&(((structure*)0)->item) is the address of that mythical object at address 12.

Finally, ((u16)&(((structure*)0)->item)) is memory address 12 (of type structure*) explicitly converted to u16. Presumably u16 is an unsigned 16-bit integer type (if so, it would make more sense to use the standard uint16_t).

So given a structure type structure, and a member item at offset 12, this macro would expand to an expression that yields the value 12.

Keep in mind that addresses (pointer values) are not numbers; they're conceptually distinct concepts. This macro will work only for systems that use a particularly memory model, one that isn't specified by the C standard.

I'm not sure why the result is of type u16. The standard offsetof macro yields a value of type size_t.

For systems that use a different memory model (for example, if a null pointer is represented as all-bits-one or 0xdeadbeef), this OFFSETOF macro would not work, and the standard offsetof macro would have to tailored to work on that particular system. That's part of the reason offsetof is in the standard library, so it can be implemented in a way that works on each system.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631