tags:

views:

40

answers:

3

Please take a look at this macro. It is used in Symbian OS SDK, which compiler is based on GCC (< 4 version of it).

#ifndef _FOFF
#if __GNUC__ < 4
#define _FOFF(c,f)          (((TInt)&(((c *)0x1000)->f))-0x1000)
#else
#define _FOFF(c,f)          __builtin_offsetof(c,f)
#endif
#endif

I understand that it is calculating offset to specific class/struct member. But I cannot understand how that weird statement works - what is the constant 0x1000 and why is it there? Could somebody please explain this to me?

+1  A: 

It is working out the relative address of 'f' as a member of a class/struct at address 0x1000, and then subtracting 0x1000 so that only the difference between the class/struct address and the member function address is returned. I imagine a non-zero value (i.e. the 0x1000) is used to avoid null pointer detection.

sje397
Thanks, it is interesting idea - about non-zero value purpose.
Haspemulator
+1  A: 

"If there was a member of struct c starting exactly at the (perfectly-aligned;-) address 0x1000, then at what address would the struct's member f be?" -- answer: the offset you're looking for, minus of course the hypothetical starting address 0x1000 for the struct... with the difference, AKA distance or offset, computed as integers, otherwise the automatic scaling in address arithmetic throws you off (whence the cast).

What parts of the expression, specifically, are giving you problems?

The inner part &(((c *)0x1000)->f) is "the address of member f of a hypothetical struct c located at 0x1000. Right in front of it is the cast (I assume TInt is some kind of integer type, of course), then the - 0x1000 to get the offset (AKA distance or difference between the address of the specific member of interest and the start of the whole structure).

Alex Martelli
Thanks, your answer gave me enough details to understand this macro. But could you please explain me what did you mean in this sentence: 'otherwise the automatic scaling in address arithmetic throws you off (whence the cast).' Basically, about automatic scaling.
Haspemulator
@Haspem, for example, `((int*)0x1008) - ((int*)0x1000)` is _not_ `8`, it's `8/sizeof(int)` (for a total of 2 on most modern architectures, 4 on some old 16-bit ones, maybe 1 on some 64-bit ones). In C, address arithmetic is always scaled by the `sizeof` of whatever type we're taking addresses of!-) So, to get address arithmetic in bytes, with no scaling, cast the addresses to integers (or `char*`, I guess, since, by C language standards, `sizeof(char)` _must_ be 1;-).
Alex Martelli
Thanks for elaboration! I try to imagine now, what would be the result without the cast left part to integer. At the right side is a integer, so we subtract integer from address here. I think that the result would be `address_of_f_member - 0x1000 / sizeof(f_member)`. Am I right?
Haspemulator
@Haspem, no, "pointer to whatever" minus integer is just a type error by the rules of the C language.
Alex Martelli
Thanks! I've just checked this in compiler (obviously, I should do this earlier instead of asking such a stupid question :) ).
Haspemulator
+2  A: 

Imo 0x1000 is just a randomly chosen number. It is not a valid pointer, and it you could probably use zero instead of it.

How it works:

  1. Casts 0x1000 into class pointer (pointer of type c). - (c*)0x1000
  2. Takes pointer to "f" member of class c - &(((c *)0x1000)->f)
  3. Casts it into TInt. ((TInt)&(((c *)0x1000)->f))
  4. Substracts integer value of pointer to base (0x1000 in this case) from integer value of pointer to c's member: (((TInt)&(((c *)0x1000)->f))-0x1000)

Becuase f isn't being written to, there is no accessViolation/segfault.

You could probably use zero instead of 0x1000 and discard subtraction (i.e. just use "((TInt)&(((c *)0x0000)->f))"), but maybe author thought think that subtracting base pointer from pointer to member is a more "proper" way than trying to directly cast pointer into integer. Or maybe compiler provides "hidden" class members that can have negative offset (which is possible in some compilers - for example Delphi Compiler (I know it isn't c++) provided multiple hidden "fields" that were located before "self"(analogue of "this") pointer), in which case using 0x1000 instead of 0 makes sense.

SigTerm