[comp.lang.c] offsetof

am@cl.cam.ac.uk (Alan Mycroft) (10/12/87)

In article <1151@haddock.ISC.COM> karl@haddock.ima.isc.com (Karl Heuer) wrt:
>>I see some benign applications for constant expressions involving null
>>	(char *)&((struct foo *)0)->bar - (char *)&((struct foo *)0)->baz
>Now, it may well be the case that "offsetof" is defined in <stddef.h> to
>expand into the above mess, on machines where it happens to work.  That's
>okay; the use is portable even though the implementation is not.
I have a concern about this.  ANSI (Oct 86) specifically disallows
unary-& from appearing in constant expressions (no doubt
except in sizeof() context).  Thus offsetof cannot expand into
the above mess since it has to be a constant expression, e.g. for
initialisers.

My current hack is to take the view that &((struct foo *)0)->bar)
is a constant expression (pace ANSI) to allow offsetof() to be definable
by macro at all.

Apart from the arguably worse suggestion that offsetof expands into 'funny'
reserved words beginning with '_' does anyone have a ANSI-legal version of
offsetof() for any machine (e.g. vax-like)?  I suppose I reckon it is
impossible, but requires much legalistic reading of dpANS.

franka@mmintl.UUCP (Frank Adams) (10/21/87)

In article <1076@jenny.cl.cam.ac.uk> am@cl.cam.ac.uk (Alan Mycroft) writes:
|In article <1151@haddock.ISC.COM> karl@haddock.ima.isc.com (Karl Heuer) wrt:
|>>	(char *)&((struct foo *)0)->bar - (char *)&((struct foo *)0)->baz
|>Now, it may well be the case that "offsetof" is defined in <stddef.h> to
|>expand into the above mess, on machines where it happens to work.
|I have a concern about this.  ANSI (Oct 86) specifically disallows
|unary-& from appearing in constant expressions (no doubt
|except in sizeof() context).  Thus offsetof cannot expand into
|the above mess since it has to be a constant expression, e.g. for
|initialisers.

I don't have a copy of the proposed standard available, but I would guess
that it does *not* disallow unary & in constant expressions.  I would guess
that, instead, it merely does not require that use of unary & be supported.
So a compiler writer would be perfectly free to support it, and then
implement offsetof in this way.
-- 

Frank Adams                           ihnp4!philabs!pwa-b!mmintl!franka
Ashton-Tate          52 Oakland Ave North         E. Hartford, CT 06108

peter@sugar.UUCP (Peter da Silva) (10/25/87)

In article <2500@mmintl.UUCP>, franka@mmintl.UUCP (Frank Adams) writes:
> In article <1076@jenny.cl.cam.ac.uk> am@cl.cam.ac.uk (Alan Mycroft) writes:
> |I have a concern about this.  ANSI (Oct 86) specifically disallows
> |unary-& from appearing in constant expressions (no doubt
> |except in sizeof() context)....

Does this mean the following use of & in &STD_Gadget[n], which is being
used in a context that requires a constant expression, is not ANSI standard?

#define G(n) &STD_Gadget[n]
...other #defines removed for terseness...

static struct Gadget STD_Gadget[GADGETS] = {
/*NEXT, LFT, TP,WDTH, H, FLAG,  ACT, TYP, REND, Z, TXT, Z, SPEC, ID, Z */
{ G(1), IN1,TP1, WD1,HT, SF,     SA,  SG,    Z, Z,   Z, Z, SI(0), 0, 0 },
{ G(2), IN1,TP2, WD1,HT, SF|SEL, SA,  SG,    Z, Z,   Z, Z, SI(1), 1, 0 },
{ G(3), IN1,TP3, WD1,HT, SF,     SA,  SG,    Z, Z,   Z, Z, SI(2), 2, 0 },
{ NULL, IN5,TP5, WD5,HT5,PF,     PA,  PG, IMAG, Z,   Z, Z,  PROP, 3, 0 }
};

Are you trying to tell me that building a linked list statically like this
is not valid 'C' code? One of the great advantages 'C' has over *any* other
conventional high level language[1] I know of is the ability to build complex
data structures statically.
--
[1] I hope you don't think Lisp, Forth, and APL are conventional languages.
-- 
-- Peter da Silva  `-_-'  ...!hoptoad!academ!uhnix1!sugar!peter
-- Disclaimer: These U aren't mere opinions... these are *values*.

chris@mimsy.UUCP (Chris Torek) (10/26/87)

>>In article <1076@jenny.cl.cam.ac.uk> am@cl.cam.ac.uk (Alan Mycroft) writes:
>>|... ANSI (Oct 86) specifically disallows unary-& from appearing in
>>|constant expressions ....

In article <907@sugar.UUCP> peter@sugar.UUCP (Peter da Silva) writes:
>Does this mean the following use of & in &STD_Gadget[n], which is being
>used in a context that requires a constant expression, is not ANSI standard?

[Example in which & is legal according to K&R, but not according to
the aforementioned draft ANS.]

It *was* not, but the committee appears to be reinstating K&R rules:
that an expression that reduces to a previously declared global or
static variable, optionally with an integer constant offset, is a
constant expression.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

chris@mimsy.UUCP (Chris Torek) (10/27/87)

In article <9105@mimsy.UUCP> I wrote, in part:
>... an expression that reduces to a previously declared global or
>static variable, optionally with an integer constant offset, is a
>constant expression.

Oops, I lost a phrase.  Add `the address of' between `reduces to' and
`a previously declared'.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

mccaugh@uiucdcsb.UUCP (10/28/87)

 Just an observation: since your objection is couched in a #define, I wonder:
 does the ANSI C standard pertain just as strictly to the pre-rpocessor (cpp)?

 mccaugh@uiucmsl

nextuid@xyzzy.UUCP (10/30/87)

In article <907@sugar.UUCP> peter@sugar.UUCP (Peter da Silva) writes:
| In article <2500@mmintl.UUCP>, franka@mmintl.UUCP (Frank Adams) writes:
| > In article <1076@jenny.cl.cam.ac.uk> am@cl.cam.ac.uk (Alan Mycroft) writes:
| > |I have a concern about this.  ANSI (Oct 86) specifically disallows
| > |unary-& from appearing in constant expressions (no doubt
| > |except in sizeof() context)....
| 
| Does this mean the following use of & in &STD_Gadget[n], which is being
| used in a context that requires a constant expression, is not ANSI standard?

To try and quell the followups, here is the wording from the August 3 draft
(page 49) pertaining to constant expressions.

      ....

          The operands in an integral constant expression shall consist
      only of integer, enumeration, and character constants, sizeof
      expressions, and casts of aritmetic types to integral types.  The
      array-subscript [] and member-acess . and -> operators, the
      address & and indirection * unary operators, and arbitrary casts
      may appear only in the operand of a sizeof operator[36].  The
      value of an integral constant expression shall be representable in
      the storage appropriate for its type in the execution environment.

          More latitude is permitted for constant expressions in
      initializers.  Floating constants and arbitrary casts may also be
      used.  Lvalues designating objects that have static storage
      duration or function designators may also be used to specify
      addresses, explicitly with the unary & operator, or implicitly,
      for expressions of array or function type.

      ....

      [36] The operand of a sizeof operator is not evaluated.


Seems clear enough to me.  At the last meeting, it was voted to restore
the constraint that accidently got deleted stating that when you use an
address in an initializer, you can only add or subtract and integral
constant to it  (ie, you can't do:  int i = ((int) &j) * 50;).

jas@ernie.Berkeley.EDU (Jim Shankland) (11/10/88)

In article <481@njsmu.UUCP> klg@njsmu.UUCP (Kenneth Goodwin) writes:
>Until they actually stick something like [offsetof] into the compiler, try
>this:
>
>#define	offsetof(Struct, Member)	(&((struct Struct *)0)->Member)
>
>...
>
>	Since all kludges have an inherent risk of non-portability
>	comments on the above method are welcome.

Alas, I've run into C compilers on a few UNIX machines that barf on this.
Not about the syntax itself -- they just don't want you dereferencing
the null pointer (even though you're not, really).  Which means that for
maximal portability, this won't work either.

On such machines, you must have an instance of the structure:

struct mumble foo;
int mem_offset = (char *)(&foo.member) - (char *)(&foo);

Sigh.  The things you have to put up with in the portability trenches.
This is about the 10,000th reason that we need ANSI C *now*.

Jim Shankland
jas@ernie.berkeley.edu

rbutterworth@watmath.waterloo.edu (Ray Butterworth) (11/16/88)

In article <26740@ucbvax.BERKELEY.EDU>, jas@ernie.Berkeley.EDU (Jim Shankland) writes:
> On such machines, you must have an instance of the structure:
> struct mumble foo;
> int mem_offset = (char *)(&foo.member) - (char *)(&foo);
> Sigh.  The things you have to put up with in the portability trenches.


The same argument applies for the alignof macro:

#define alignof(type) /* assumes double is max aligned */ \
    ( (char*)&(((struct {double _d; char _c; type _t;}*)0)->_t) \
    - (char*)&(((struct {double _d; char _c; type _t;}*)0)->_c) )

which is equally non-portable,
but for some reason the Committee chose not to make this macro
part of the Standard even though alignof() seems to be more useful
(to me at least) than offsetof().


> This is about the 10,000th reason that we need ANSI C *now*.
And this is one more reason why "ANSI C now" still isn't good enough.

gwyn@smoke.BRL.MIL (Doug Gwyn ) (11/17/88)

In article <22164@watmath.waterloo.edu> rbutterworth@watmath.waterloo.edu (Ray Butterworth) writes:
>... for some reason the Committee chose not to make this macro
>part of the Standard even though alignof() seems to be more useful
>(to me at least) than offsetof().

As has been explained many times before, and will be explained yet again
in the third-round response document, alignment can depend on context,
so alignof(type) is not realizable in some implementations.

>And this is one more reason why "ANSI C now" still isn't good enough.

I think having a standard drafted by people who understand these issues
will be a great service to the C community.

dave@motto.UUCP (dave brown) (08/25/89)

I think I heard somewhere of an offsetof() macro that was proposed for
the ANSI C Standard.  If I've got it right, it would return the
number of bytes from the start of the structure to a given member, i.e.
the offset of the member within the structure.

My questions are:

1. Am I right about such a macro being proposed?

2. Was it accepted?

3. What arguments does it take?  What value does it return?

4. Can it be written for all compilers?  Can one version be portable,
   or would different versions have to be written for different compilers?

5. Can it be used in a static initializer, ex:

	size_t mbr_off = offsetof(...);

5. If it could be portable, can you supply a definition.  If not, can you
   supply one which would work in most cases?

Thanks
	Dave

 -----------------------------------------------------------------------------
|  David C. Brown		|  uunet!mnetor!motto!dave		      |
|  Motorola Canada, Ltd.	|  416-499-1441 ext 3708		      |
|  Communications Division	|  Disclaimer: Motorola is a very big company |
 -----------------------------------------------------------------------------

davidsen@crdos1.crd.ge.COM (Wm E Davidsen Jr) (08/25/89)

  I think I asked this before, but I didn't see any answers. If you wish
feel free to mail instead of post.

  What good is offsetof? What wonderful things can I do with it that I
can't do without it, and are they things the average system hacker would
want to do. The only use for it which comes to mind is playing with
individual fields in a file  of structs, hardly common enough to justify
mandating a feature in the language.

  I read 4.1.5 and there is no info on what great things this does for
me. I have great respect for the committee and I'm sure there's a reason
why this was added, but it escapes me.

-- 
bill davidsen	(davidsen@crdos1.crd.GE.COM -or- uunet!crdgw1!crdos1!davidsen)
"The world is filled with fools. They blindly follow their so-called
'reason' in the face of the church and common sense. Any fool can see
that the world is flat!" - anon

dfp@cbnewsl.ATT.COM (david.f.prosser) (08/25/89)

In article <70@motto.UUCP> dave@motto.UUCP (dave brown) writes:
>My questions are:
>
>1. Am I right about such a macro [offsetof] being proposed?

Yes.

>2. Was it accepted?

Yes.

>3. What arguments does it take?  What value does it return?

	#include <stddef.h>
	size_t offsetof(_type_, _member_);
where
	if given ``static _type_ t;'', then ``&(t._member_)'' must
	be a valid address constant.

>4. Can it be written for all compilers?  Can one version be portable,
>   or would different versions have to be written for different compilers?

There is no known valid C expression that meets all the requirements
for a portable offsetof macro.  This is why it was standardized as
such, instead of giving enough power to constant expressions.

>5. Can it be used in a static initializer, ex:
>
>	size_t mbr_off = offsetof(...);

Yes.

>5. If it could be portable, can you supply a definition.  If not, can you
>   supply one which would work in most cases?

The Rationale suggests some possibilities:

	(size_t)&(((_type_*)0)->_member_)
	(size_t)(char *)&(((_type_*)0)->_member_)
	(size_t)(((char *)&(((_type_*)0)->_member_))-((char *)0))
	(size_t)(((char *)&(((_type_*)&X)->_member_))-((char *)&X))

where in the last X is some predeclared static object address.

None of these are portable.

Dave Prosser	...not an official X3J11 answer...

rgh@inmet (08/26/89)

>I think I heard somewhere of an offsetof() macro that was proposed for
>the ANSI C Standard.  If I've got it right, it would return the
>number of bytes from the start of the structure to a given member, i.e.
>the offset of the member within the structure.

That's right.

> 1. Am I right about such a macro being proposed?
> 2. Was it accepted?

Yes, it's part of the standard;  it's defined in the <stddef.h> header.

> 3. What arguments does it take?  What value does it return?
>5. Can it be used in a static initializer, ex:
>	size_t mbr_off = offsetof(...);

offsetof( type, member-designator)

It expands to an integral constant expression that has type size_t
(the same type that sizeof returns).  Since it's a constant expression,
it can be used as a static initializer.

>4. Can it be written for all compilers?  Can one version be portable,
>   or would different versions have to be written for different compilers?
>5. If it could be portable, can you supply a definition.  If not, can you
>   supply one which would work in most cases?

One reason that it was standardized as a macro is that there didn't
seem to be any definition to cite that would work on all implementations.

These may work in a lot of implementations, and will not work in some:
(starting out with #define offsetof(s_name, m_name) )

    (size_t)&(((s_name*)0)->m_name)

    (size_t)(char*)&(((s_name*)0)->m_name)

or, where X is some predeclared address (or possibly 0) and
A(Z) is defined as ((char*)&Z):

    (size_t)( A( (s_name)X->m_name ) - A( X ) )

It's also possible to expand this to a special function call recognized
by the compiler: the compiler can then just rummage through its symbol
table & spit out the right answer.

	Randy Hudson   uunet!inmet!rgh

gwyn@smoke.BRL.MIL (Doug Gwyn) (08/26/89)

In article <70@motto.UUCP> dave@motto.UUCP (dave brown) writes:
>1. Am I right about such a macro being proposed?
>2. Was it accepted?

Yes.

>3. What arguments does it take?  What value does it return?

#include <stddef.h> defines the offsetof macro, which has two arguments:
the first designates a structure type, and the second is a member
designator.  It returns the offset (in bytes) of the member of such a
structure.

>4. Can it be written for all compilers?  Can one version be portable,
>   or would different versions have to be written for different compilers?

In general, some help from the compiler is required.  This could take the
form of a keyword __offsetof in terms of which the offsetof macro could
be defined.

An almost-portable definition is possible, using a single extern struct
and a lot of type casts and so forth.  You may also have seen a definition
that involves using 0 as a pseudo-structure pointer; that's less likely to
work.

>5. Can it be used in a static initializer, ex:
>	size_t mbr_off = offsetof(...);

Frankly, I don't recall, and my copy of the standard is not at hand.

>5. If it could be portable, can you supply a definition.  If not, can you
>   supply one which would work in most cases?

I'm sure you'll get zillions of suggestions here..

gwyn@smoke.BRL.MIL (Doug Gwyn) (08/28/89)

In article <175@crdos1.crd.ge.COM> davidsen@crdos1.crd.ge.COM (Wm E Davidsen Jr) writes:
>  What good is offsetof?

Extracted from <sys/dirent.h>:

struct dirent				/* data from getdents()/readdir() */
	{
	long		d_ino;		/* inode number of entry */
	off_t		d_off;		/* offset of disk directory entry */
	unsigned short	d_reclen;	/* length of this record */
	char		d_name[1];	/* name of file */	/* non-POSIX */
	};

/* The following nonportable ugliness could have been avoided by defining
   DIRENTSIZ and DIRENTBASESIZ to also have (struct dirent *) arguments. */
#define	DIRENTBASESIZ		(((struct dirent *)0)->d_name \
				- (char *)&((struct dirent *)0)->d_ino)
#define	DIRENTSIZ( namlen )	((DIRENTBASESIZ + sizeof(long) + (namlen)) \
				/ sizeof(long) * sizeof(long))

This, as the comment indicates, is nonportable and ugly.  I leave it as a
simple exercise to the reader to figure out how offsetof() can improve this.

gwyn@smoke.BRL.MIL (Doug Gwyn) (08/28/89)

In article <1629@cbnewsl.ATT.COM> dfp@cbnewsl.ATT.COM (david.f.prosser) writes:
>	(size_t)(((char *)&(((_type_*)&X)->_member_))-((char *)&X))
>where in the last X is some predeclared static object address.
>None of these are portable.

But this last one is pretty close, if X is changed to __X and made
an extern struct { double __d; char __pad[__UMPTEEN]; } __X; which has
probably pessimal alignment constraints (you could change the double to
a fancy union to make sure).  The Standard allows pointers to tough
alignments to be converted to pointers to easy alignments without problem.
The only practical problem remaining would be if the offset to the member
exceeded the valid pointer range for the __X object and if this mattered
(as it might, on a segmented architecture); if you make __UMPTEEN big
enough this problem could be avoided, at the cost of wasted data space.
(Perhaps the space could hold genuine C library data objects, with __X
just an entry point labeling the beginning of the data space.)

vohra@uts.amdahl.com (Pavan Vohra) (03/10/90)

I would like to compile in the offsets of structure members.

For example, with this structure

    struct astruct {
      int member0;
      char member1;
    } myastruct;

I want to have, in the same program that the structure appears in,
the number of bytes past myastruct where I can find member1.

The application is an interpreter that will allow one to specify
at run time the structure name and the member name, and to print
the value of structure.member as a decimal or a string depending
on its type.  I will create an array with the offsets and names
of the members and what structures they are members of.  Once this
part of the program is done I will want to write a program that
creates the array.

Do you think that that is a good way to do it?

Has anybody implemented a virtual offsetof() compile-time operator,
or an application similar to mine?  I suppose symbolic debuggers
do it all the time.

-- 
---
Pavan Vohra
Advanced Design Automation Technologies
Amdahl Corporation                     {..hplabs|ames|decwrl}!amdahl!vohra
Sunnyvale, CA 94086-3470               {vohra@uts.amdahl.com}
---

amoss@batata.huji.ac.il (amos shapira) (03/13/90)

vohra@uts.amdahl.com (Pavan Vohra) writes:

>I would like to compile in the offsets of structure members.

>For example, with this structure

>    struct astruct {
>      int member0;
>      char member1;
>    } myastruct;

>I want to have, in the same program that the structure appears in,
>the number of bytes past myastruct where I can find member1.

The answer could be simple and I used several times, do this:

struct astruct *ap;

ap = NULL;

offsetof_member0 = &ap->member0; /* zero offset */
offsetof_member1 = &ap->member1; /* sizeof(member0) */

An offsetof() operator would be quite handy here. But until then you can
use this with a good portability of your programs.

Now comes to mind, maybe you could avoid the nbull pointer variable with this
(didn't try it):

offsetof_member0 = &((struct astruct *)NULL->member0);
offsetof_member1 = &((struct astruct *)NULL->member1);

- Amos Shapira

amoss@batata.bitnet
amoss@batata.huji.ac.il

ts@cup.portal.com (Tim W Smith) (03/18/90)

< The answer could be simple and I used several times, do this:
< 
< struct astruct *ap;
< 
< ap = NULL;
< 
< offsetof_member0 = &ap->member0; /* zero offset */
< offsetof_member1 = &ap->member1; /* sizeof(member0) */
< 
< An offsetof() operator would be quite handy here. But until then you can
< use this with a good portability of your programs.

It IS simple.  It is also wrong.

If you want to have at least a small chance of working across different
machines, try this:

	offsetof_member0 = (ulong)&ap->member0 - (ulong)ap;
	...
						Tim Smith

seanf@sco.COM (Sean Fagan) (03/19/90)

In article <28008@cup.portal.com> ts@cup.portal.com (Tim W Smith) writes:
>It IS simple.  It is also wrong.
>
>If you want to have at least a small chance of working across different
>machines, try this:
>
>	offsetof_member0 = (ulong)&ap->member0 - (ulong)ap;

You mean like a '386 Large model program (or a Cyber 180-state, take your
pick)?  Where sizeof(ulong) == 4 and sizeof(void *) == (6 or 8, depending on
machine)?  (Well, ok, the example wouldn't cause problems on the '386, but
it could, I think, on the Cyber...)

If you want to *try* to be portable, see if your machine has a ptrdiff_t
somewhere (I'm assuming non-ANSI, since, otherwise you would automatically
use ptrdiff_t and offsetof()), or, if you have gcc, use the typeof
extension.  Otherwise, hack it 8-).

-- 

-----------------+
Sean Eric Fagan  | "Time has little to do with infinity and jelly donuts."
seanf@sco.COM    |    -- Thomas Magnum (Tom Selleck), _Magnum, P.I._
(408) 458-1422   | Any opinions expressed are my own, not my employers'.

meissner@osf.org (Michael Meissner) (03/20/90)

In article <28008@cup.portal.com> ts@cup.portal.com (Tim W Smith)
writes:

| < The answer could be simple and I used several times, do this:
| < 
| < struct astruct *ap;
| < 
| < ap = NULL;
| < 
| < offsetof_member0 = &ap->member0; /* zero offset */
| < offsetof_member1 = &ap->member1; /* sizeof(member0) */
| < 
| < An offsetof() operator would be quite handy here. But until then you can
| < use this with a good portability of your programs.
| 
| It IS simple.  It is also wrong.
| 
| If you want to have at least a small chance of working across different
| machines, try this:
| 
| 	offsetof_member0 = (ulong)&ap->member0 - (ulong)ap;
| 	...
| 						Tim Smith

Hmmm, in order to have a better chance of working, try:

	offsetof_member0 = ((char *)&ap->member0) - ((char *)ap);

This will work on machines which have weird pointer formats (such as
the Data General MV, the PR1ME 50 series, and the 80*86 in HUGE mode),
and will also work on machines which have pointers larger than the
largest integer type.

--
Michael Meissner	email: meissner@osf.org		phone: 617-621-8861
Open Software Foundation, 11 Cambridge Center, Cambridge, MA

Catproof is an oxymoron, Childproof is nearly so

jfc@athena.mit.edu (John F Carr) (03/05/91)

Two questions about offsetof.

1. Is the following legal?

	struct foo { struct bar { int b;} a;};
	offsetof (struct foo, a.b);

The X toolkit source code assumes that offsetof (struct foo, a.b) is legal.
The compiler I use disagrees.  I think it is right, but I'm not sure.

2. On how many compilers does the following trick to emulate offsetof not work:
	(int)&((struct foo*)0)->member

How many of those do not provide an offsetof macro?

(I know of only one compiler which doesn't like the version using a NULL
pointer (Metaware High C 1.4r for the IBM RT), and that compiler provides
an offsetof macro).

 
--
    John Carr (jfc@athena.mit.edu)

mccrady@torolab6.vnet.ibm.com ("++Don;") (05/21/91)

> That is, ((size_t)&(((type *)0)->member)) or something like that.
> I had thought the expression is evaluated at run-time because
> the following expression is not accepted:
>         char    a_array[offsetof(type,member)];

If offsetof() is defined as you say, then its use in the array index
is illegal according to ANSI.  ANSI requires that the array dimension
be an integral constant expression.  Section 3.5 states:

      Cast operators in an integral constant expression shall
      only convert arithmetic types to integral types, except
      as part of an operand to the sizeof operator.

Your compiler's definition of offsetof() converts pointer types to
integral types, and thus violates the above constraint.

I know that's not very helpful to you, but it at least answers your
question.

++Don;