[net.lang.c] handy.h: macro

tp@wucs.UUCP (tom thumbs) (09/11/85)

    The macro OFFSETV is good for finding the offset of a
certain variable within a structure. OFFSET can also be used,
if one is careful to hand it an address. [ hopefully, the
example below will explain what this is doing.]
    (As noted below, these should be used carefully.)
    Anyway, I found these exceedingly useful when doing lots of
database stuff with intricate structures.
    I believe that credit for coming up with these belongs to
Brian Thomas of AT&T (ihnp4!we53!bmt).
---------------------handy macro & example below ----------
#include <stdio.h>

/* the two lines below contain the wild macros -- cautious people
    will put parens are the X and Y on right-hand side */
#define OFFSET(X, Y)	( (int) ((struct X *)0)-> Y )
#define OFFSETV(X, Y)	( (int) &((struct X *)0)-> Y )

struct foo {		/* simple example structure type	*/
    int harris;
    char torek[6];
    long gwyn;
} ;

main()
{
    printf(" offset harris=%d, gwyn=%d, torek=%d, alt. torek=%d\n",
	OFFSETV(foo, harris), 
	OFFSETV(foo, gwyn), 
	OFFSETV(foo, torek[0]),
	OFFSET(foo, torek) );
}

-----------------------------------------------------------------

   On VAX 750 with 4.2 BSD (version Kurt) we get output of:

 offset harris=0, gwyn=12, torek=4, alt. torek=4

    (note gwyn is word-aligned, of course)
--------
WARNINGS: may cause weird alignment problems!! Also, you better
    know the difference between arrays and pointers (in other words,
    this is dangerous for novices, and probably others as well).
--
   ...tp...
       tom patterson
	   ... {seismo, ihnp4}!wucs!tp

chris@umcp-cs.UUCP (Chris Torek) (09/12/85)

In article <1153@wucs.UUCP> tp@wucs.UUCP (tom thumbs) writes:

> The macro OFFSETV is good for finding the offset of a
> certain variable within a structure. [...]

> #define OFFSETV(X, Y)	( (int) &((struct X *)0)-> Y )

> I believe that credit for coming up with these belongs to
> Brian Thomas of AT&T (ihnp4!we53!bmt).

Actually, that technique has been used in 4BSD for quite a while.
/sys/vax/genassym.c declares some pointers to structures, puts
zeros in them, and uses addresses of their fields to print out code
suitable for input to the assembler to define the offsets to fields
that are required by the assembly-coded portion of the kernel.
(Later, if the definitions of those structures are changed, no
code need be changed.)

I might point out (as Guy is likely in the process of doing right
now as well) that this, like so much other useful code, is (alas!)
not portable.  Consider a hypothetical byte-addressable 32 bit
machine where the ``null `struct foo' pointer'' has a bit pattern
whose value is 0xc0000000 when taken as a ``int''.  Then the compiler
will cast 0 to ``struct foo *'', yeilding 0xc0000000, add to that
the offset for the given field, and pun that into an integer, giving
results like 0xc0000004.

(What C compiler would do this?  Well, a Vax compiler just might.
Zero *is* a legal address, after all, and 0xc0000000 is not....)

On such a machine,

#define OFFSETV(x,y)	\
	((int) &((struct x *)0)->y - (int) ((struct x *)0))

would suffice to eliminate the offset; but this might fail on
a machine that was not byte addressable.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 4251)
UUCP:	seismo!umcp-cs!chris
CSNet:	chris@umcp-cs		ARPA:	chris@maryland