Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > Using offsetof to create a pointer to the start of a struct?

Reply
Thread Tools

Using offsetof to create a pointer to the start of a struct?

 
 
Not Really Me
Guest
Posts: n/a
 
      03-13-2008
We have run into some code that is using a hand calculated offset to
dereference a pointer to a struct element to create a pointer to the start
of the struct.

(Example code below), basically a function is passed a pointer to a struct
member that is not the first member in the struct. The function actually
needs a pointer to the struct itself. To get that pointer, the code is
subtracting the hand calculated offset of the member, from the from the
pointer to the member, to create a pointer to the start of the struct. Ugly?

The problem we see is that c99 6.3.2.3p5 says that subtracting an int from a
pointer that is not pointing to an array is implementation dependent. In
theory this should be portable code.

To overcome this, someone suggested using the C99 offsetof macro to do get
the offset. Otherwise the math remains the same.

Is using offsetof still a violation of 6.3.2.3p5?

Is so, is there any safe way to do this (short of rewriting the code to pass
a pointer to the start of the struct)?

struct { int apples, int oranges, int lemons, int grapes, int limes } Fruit;

struct Fruit *pfruit;

foo( pfruit->grapes);

void foo( int *purple_fruit )
{
struct Fruit *local_fruit;
int apple;

local_fruit = &purple_fruit->grapes - offsetof( Fruit, grapes);
apple = local_fruit->apples;
}

Scott


 
Reply With Quote
 
 
 
 
Peter Nilsson
Guest
Posts: n/a
 
      03-14-2008
On Mar 14, 10:03*am, "Not Really Me" wrote:
> We have run into some code that is using a hand
> calculated offset to dereference a pointer to a
> struct element to create a pointer to the start
> of the struct.
>
> (Example code below), basically a function is passed
> a pointer to a struct member that is not the first
> member in the struct. *The function actually needs a
> pointer to the struct itself. *To get that pointer,
> the code is subtracting the hand calculated offset
> of the member, from the from the pointer to the member,
> to create a pointer to the start of the struct. Ugly?


Yes, offsetof is definitely better. But this sort of
thing can be dangerous and errors can be hard to debug.

> The problem we see is that c99 6.3.2.3p5 says that
> subtracting an int from a pointer that is not pointing
> to an array is implementation dependent.


No, 6.3.2.3p5 talks about conversion of an integer to
a pointer.

>*In theory this should be portable code.


Not quite.

> To overcome this, someone suggested using the C99
> offsetof macro to do get the offset.


It's available in C90 too.

>*Otherwise the math remains the same.
>
> Is using offsetof still a violation of 6.3.2.3p5?


No. Subtraction of an integer from a pointer does
not require the integer to be converted to a pointer.

> Is so, is there any safe way to do this (short of
> rewriting the code to pass a pointer to the start
> of the struct)?
>
> struct { int apples, int oranges, int lemons,
> int grapes, int limes } Fruit;
>
> struct Fruit *pfruit;
>
> foo( pfruit->grapes);
>
> void foo( int *purple_fruit )
> {
> * * struct Fruit**local_fruit;
> * * int * * * * *apple;
>
> * * local_fruit = &purple_fruit->grapes -
> offsetof( Fruit, grapes);


A few things...

This takes a pointer to the parameter, which is not what
you want.

The offsetof macro takes a _type_ as the first argument,
not an object.

Pointer subtraction is in units of the element being
pointed to, whereas offsetof returns a count in bytes.
[So you need a byte/character pointer to use it
effectively.]

size_t grapes_delta = offsetof(struct Fruit, grapes);
char *grapes_ptr = (char *) purple_fruit;
local_fruit = (struct Fruit *) (grapres_ptr - grapes_delta);

Or without temporaries...

local_fruit =
(struct Fruit *)
(
((char *) purple_fruit)
- offsetof(struct Fruit, grapes)
);

Strictly speaking, I think this violates the literal
interpretation of 6.5.6 (Additive operators), but I
can't see how it violates the intent, particularly
of offsetof.

> * * apple = local_fruit->apples;
>
> }


Passing a pointer to struct Fruit may well be the better
option, not merely in terms of semantics, but also in
terms of design.

--
Peter
 
Reply With Quote
 
 
 
 
Jack Klein
Guest
Posts: n/a
 
      03-14-2008
On Thu, 13 Mar 2008 17:03:56 -0600, "Not Really Me"
<(E-Mail Removed)> wrote in comp.lang.c:

> We have run into some code that is using a hand calculated offset to
> dereference a pointer to a struct element to create a pointer to the start
> of the struct.
>
> (Example code below), basically a function is passed a pointer to a struct
> member that is not the first member in the struct. The function actually
> needs a pointer to the struct itself. To get that pointer, the code is
> subtracting the hand calculated offset of the member, from the from the
> pointer to the member, to create a pointer to the start of the struct. Ugly?
>
> The problem we see is that c99 6.3.2.3p5 says that subtracting an int from a
> pointer that is not pointing to an array is implementation dependent. In
> theory this should be portable code.


You just lost me here. In my C99, original and including TC3,
6.3.2.3, is Conversions, Other operands, Pointers, and paragraph 5
begins with the sentence "An integer may be converted to any pointer
type."

Perhaps you have mistyped the reference?

I'm looking at paragraph 8 of 6.5.6 Additive operators, which starts
with the phrase "When an expression that has integer type is added to
or subtracted from a pointer...".

> To overcome this, someone suggested using the C99 offsetof macro to do get
> the offset. Otherwise the math remains the same.
>
> Is using offsetof still a violation of 6.3.2.3p5?


Again, I don't see that the paragraph you cite has any relevance at
all.

> Is so, is there any safe way to do this (short of rewriting the code to pass
> a pointer to the start of the struct)?


There is a safe and portable way to do this using offsetof, and this
is exactly the purpose of the macro, to allow a safe and standardized
way of doing things that you can't portably do without it.

But first you have to pull together some other things from other parts
of the standard.

6.2.6.1 p4 "Values stored in non-bit-field objects of any other object
type consist of n CHAR_BIT bits, where n is the size of an object of
that type, in bytes."

6.5 p7 "An object shall have its stored value accessed only by an
lvalue expression that has one of the following types:

[snip all but last bullet item]

a character type."

Essentially, any object can be treated as an array of bytes equal to
the size of the object.

> struct { int apples, int oranges, int lemons, int grapes, int limes } Fruit;
>
> struct Fruit *pfruit;
>
> foo( pfruit->grapes);
>
> void foo( int *purple_fruit )
> {
> struct Fruit *local_fruit;
> int apple;
>
> local_fruit = &purple_fruit->grapes - offsetof( Fruit, grapes);
> apple = local_fruit->apples;
> }


If you're going to post example code, instead of real code, you should
at least make sure that it is compilable, which the above is not due
to a large number of errors.

But the complete program below:

#include <stdio.h>
#include <stddef.h>

struct Fruit { int apples; int oranges; int lemons; int grapes; int
limes; };

void foo( int *purple_fruit )
{
struct Fruit *local_fruit;
local_fruit = (struct Fruit *)((char *)purple_fruit -
offsetof(struct Fruit, grapes));

printf("local_fruit = %p\n", local_fruit);
}

int main(void)
{
struct Fruit my_fruit;
struct Fruit *pfruit = &my_fruit;

printf("pfruit = %p\n", pfruit);
foo( &pfruit->grapes);
return 0;
}

....is perfectly conforming and portable.

By casting a pointer to any member of an aggregate object to a
character type, you now effectively have a pointer into an array of
character types the size of the aggregate object. Any pointer
additions or subtractions you do are perfectly valid as long as the
result lies within that array of bytes, that is the boundaries of the
original aggregate object, or to one past the last byte.

Since the offsetof macro returns an offset, in bytes, you can directly
subtract the value yielded from a pointer to char converted from the
address of the corresponding member, and yield the address of the
first byte of the structure type. And then you can convert, with a
cast, to a pointer to a structure type and it is guaranteed to point
to the full structure.

This is 100% portable to any conforming C90 or later implementation.

> Scott


--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://c-faq.com/
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.club.cc.cmu.edu/~ajo/docs/FAQ-acllc.html
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Using offsetof() values to access members of a struct Urs Thuermann C Programming 6 05-25-2007 04:02 PM
Pointer to data member vs. offsetof Imre C++ 2 04-22-2007 09:22 AM
offsetof Tony Johansson C++ 1 12-16-2004 12:32 AM
Obtain sizeof struct element using offsetof()? Mark A. Odell C Programming 10 10-01-2004 01:24 AM
g++ "offsetof" problem Hiroki Horiuchi C++ 5 11-25-2003 05:01 PM



Advertisments