424,054 Members | 1,078 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 424,054 IT Pros & Developers. It's quick & easy.

find index, given pointer to member

P: n/a
ike
Could you please give your opinion on the portability of this code?

/* -------- begin findindex.c -------- */

struct Foo { int junk0; };
struct Bar { int junk1; struct Foo foo; int junk2; };

int findindex(struct Bar * bars, struct Foo * fooptr)
/* Pre:
* bars points to (the first element of) an array of Bar.
* fooptr points to the 'foo' member of an element of bars,
* i.e. fooptr == & bars[i].foo for some valid index i
* Returns:
* the actual value of i, as defined above.
*/
{
return ((char*)fooptr - (char*)&bars[0].foo) / sizeof *bars;
}

/* -------- end findindex.c -------- */

Ike
Nov 14 '05 #1
Share this Question
Share on Google+
11 Replies


P: n/a
ik*@iae.nl wrote:
Could you please give your opinion on the portability of this code? /* -------- begin findindex.c -------- */ struct Foo { int junk0; };
struct Bar { int junk1; struct Foo foo; int junk2; }; int findindex(struct Bar * bars, struct Foo * fooptr)
/* Pre:
* bars points to (the first element of) an array of Bar.
* fooptr points to the 'foo' member of an element of bars,
* i.e. fooptr == & bars[i].foo for some valid index i
* Returns:
* the actual value of i, as defined above.
*/
{
return ((char*)fooptr - (char*)&bars[0].foo) / sizeof *bars;
}


Looks ok to me (as long as the arguments are what you tell). An alter-
native would be

return (struct Bar*)((char*)fooptr - offset_of(struct Bar, foo)) - bars;

which avoids the (explicit) division. And did you think about returning
a size_t instead of an int (the result can't be negative or you called
it with invalid arguments)?
Regards, Jens
--
\ Jens Thoms Toerring ___ Je***********@physik.fu-berlin.de
\__________________________ http://www.toerring.de
Nov 14 '05 #2

P: n/a
ik*@iae.nl wrote:
Could you please give your opinion on the portability of this code? struct Foo { int junk0; };
struct Bar { int junk1; struct Foo foo; int junk2; };

int findindex(struct Bar * bars, struct Foo * fooptr)
/* Pre:
* bars points to (the first element of) an array of Bar.
* fooptr points to the 'foo' member of an element of bars,
* i.e. fooptr == & bars[i].foo for some valid index i
* Returns:
* the actual value of i, as defined above.
*/
{
return ((char*)fooptr - (char*)&bars[0].foo) / sizeof *bars;
}


Ow, that's convoluted. AFAICT, it's not strictly portable, but you'd be
hard put to find an implementation where it wouldn't work. It would
require willful perversity of the implementor.

Richard
Nov 14 '05 #3

P: n/a
In message <3a*************@uni-berlin.de>
Je***********@physik.fu-berlin.de wrote:
ik*@iae.nl wrote:
{
return ((char*)fooptr - (char*)&bars[0].foo) / sizeof *bars;
}


Looks ok to me (as long as the arguments are what you tell). An alter-
native would be

return (struct Bar*)((char*)fooptr - offset_of(struct Bar, foo)) - bars;

which avoids the (explicit) division.


It's worth noting that the implicit division by sizeof *bars in the latter
form is likely to be optimised better than the explicit one.

For an implicit division in a pointer subtraction, the compiler knows that
the result of the division must be exact, which allows a strength reduction
turning it into a simple modulo multiplication. Similar strength reductions
are possible for potentially non-exact divisions, but they are more
complicated.

--
Kevin Bracey, Principal Software Engineer
Tematic Ltd Tel: +44 (0) 1223 503464
182-190 Newmarket Road Fax: +44 (0) 1728 727430
Cambridge, CB5 8HE, United Kingdom WWW: http://www.tematic.com/
Nov 14 '05 #4

P: n/a
Richard Bos wrote:

ik*@iae.nl wrote:
Could you please give your opinion on the portability of this code?

struct Foo { int junk0; };
struct Bar { int junk1; struct Foo foo; int junk2; };

int findindex(struct Bar * bars, struct Foo * fooptr)
/* Pre:
* bars points to (the first element of) an array of Bar.
* fooptr points to the 'foo' member of an element of bars,
* i.e. fooptr == & bars[i].foo for some valid index i
* Returns:
* the actual value of i, as defined above.
*/
{
return ((char*)fooptr - (char*)&bars[0].foo) / sizeof *bars;
}


Ow, that's convoluted. AFAICT, it's not strictly portable,
but you'd be
hard put to find an implementation where it wouldn't work. It would
require willful perversity of the implementor.


Are you refering to the difference
exceding the range of ptrdiff_t?

--
pete
Nov 14 '05 #5

P: n/a
ike
Je***********@physik.fu-berlin.de wrote:
ik*@iae.nl wrote:
Could you please give your opinion on the portability of this code? struct Foo { int junk0; };
struct Bar { int junk1; struct Foo foo; int junk2; }; int findindex(struct Bar * bars, struct Foo * fooptr)
/* Pre:
* bars points to (the first element of) an array of Bar.
* fooptr points to the 'foo' member of an element of bars,
* i.e. fooptr == & bars[i].foo for some valid index i
* Returns:
* the actual value of i, as defined above.
*/
{
return ((char*)fooptr - (char*)&bars[0].foo) / sizeof *bars;
}
Looks ok to me (as long as the arguments are what you tell). An alter-
native would be return (struct Bar*)((char*)fooptr - offset_of(struct Bar, foo)) - bars;
In fact that was one of the solutions I made up myself earlier, but
some compilers bark at the cast from a pointer type with loose alignment
requirements (here: char *) to a pointer type with stricter alignment
requirements (here: struct Bar *), and I wanted the code to compile
cleanly on all platforms.

<nitpick> s/offset_of/offsetof/ </nitpick>
<nitpick> offsetof() needs stddef.h </nitpick>
which avoids the (explicit) division. And did you think about returning
a size_t instead of an int (the result can't be negative or you called
it with invalid arguments)?


Yes I did, but I wanted to keep the posted example as minimal as possible.
Using size_t would have required "#include <stddef.h>".
Nov 14 '05 #6

P: n/a
ik*@iae.nl wrote:

Je***********@physik.fu-berlin.de wrote:
did you think about returning a size_t

Yes I did,
but I wanted to keep the posted example as minimal as possible.
Using size_t would have required "#include <stddef.h>".


But with your explanation included,
it winds up being less than minimal.

And with this question:
"Could you please give your opinion on the portability of this code?"
you knew that the size_t issue was going to come up.

--
pete
Nov 14 '05 #7

P: n/a
ike
pete <pf*****@mindspring.com> wrote:
ik*@iae.nl wrote:

Je***********@physik.fu-berlin.de wrote:
> did you think about returning a size_t
Yes I did,
but I wanted to keep the posted example as minimal as possible.
Using size_t would have required "#include <stddef.h>".
But with your explanation included,
it winds up being less than minimal.
And with this question:
"Could you please give your opinion on the portability of this code?"
you knew that the size_t issue was going to come up.


The portability question was about the return expression,
apparently I should have stated that more clearly.
The rest of the code was only added to produce a small compilable example.

Kind regards,
Ike
Nov 14 '05 #8

P: n/a
ik*@iae.nl wrote:

pete <pf*****@mindspring.com> wrote:
ik*@iae.nl wrote:

Je***********@physik.fu-berlin.de wrote:
> did you think about returning a size_t Yes I did,
but I wanted to keep the posted example as minimal as possible.
Using size_t would have required "#include <stddef.h>".

But with your explanation included,
it winds up being less than minimal.
And with this question:
"Could you please give your opinion on
the portability of this code?"
you knew that the size_t issue was going to come up.


The portability question was about the return expression,
apparently I should have stated that more clearly.
The rest of the code was only added to produce
a small compilable example.


The return expression
((char*)fooptr - (char*)&bars[0].foo) / sizeof *bars
contains a subexpression
(char*)fooptr - (char*)&bars[0].foo
of type ptrdiff_t.

ptrdiff_t isn't guaranteed by the standard
to be big enough to be able to represent the difference
between two pointers.
In C89 there's not that much information available
to a program about the ptrdiff_t type.

My feeling from looking at the C89 and C99 standards, is that
the intention was for ptrdiff_t to either be larger than size_t
or at the very least, to be as big as the signed version of size_t,
but there's no guarantee like that in the standard.

Even if ptrdiff_t were always at least as
big as the signed version of size_t,
your ptrdiff_t expression is in a place where it might be required
to represent the whole range of size_t.

Here's what the standard says on the issue.
N869
6.5.6 Additive operators
[#9] When two pointers are subtracted, both shall point to
elements of the same array object, or one past the last
element of the array object; the result is the difference of
the subscripts of the two array elements. The size of the
result is implementation-defined, and its type (a signed
integer type) is ptrdiff_t defined in the <stddef.h> header.
If the result is not representable in an object of that
type, the behavior is undefined.

--
pete
Nov 14 '05 #9

P: n/a
pete <pf*****@mindspring.com> wrote:
Richard Bos wrote:

ik*@iae.nl wrote:
Could you please give your opinion on the portability of this code?

struct Foo { int junk0; };
struct Bar { int junk1; struct Foo foo; int junk2; };

int findindex(struct Bar * bars, struct Foo * fooptr)
/* Pre:
* bars points to (the first element of) an array of Bar.
* fooptr points to the 'foo' member of an element of bars,
* i.e. fooptr == & bars[i].foo for some valid index i
* Returns:
* the actual value of i, as defined above.
*/
{
return ((char*)fooptr - (char*)&bars[0].foo) / sizeof *bars;
}


Ow, that's convoluted. AFAICT, it's not strictly portable,
but you'd be
hard put to find an implementation where it wouldn't work. It would
require willful perversity of the implementor.


Are you refering to the difference exceding the range of ptrdiff_t?


No, I'm referring to the comparison of two pointers which do not point
to the same (struct Foo) object. AFAICT, it would be legal if the
pointers were struct Bar pointers, since then they'd be pointers to
members of an array. Now they're pointers to members of disjunct
objects. I would be very surprised at an implementation where this were
an actual problem, of course.

Richard
Nov 14 '05 #10

P: n/a
Richard Bos wrote:
pete <pf*****@mindspring.com> wrote:
Richard Bos wrote:
ik*@iae.nl wrote:

Could you please give your opinion on the portability of this
code?

struct Foo { int junk0; };
struct Bar { int junk1; struct Foo foo; int junk2; };

int findindex(struct Bar * bars, struct Foo * fooptr)
/* Pre:
* bars points to (the first element of) an array of Bar.
* fooptr points to the 'foo' member of an element of bars,
* i.e. fooptr == & bars[i].foo for some valid index i
* Returns:
* the actual value of i, as defined above.
*/
{
return ((char*)fooptr - (char*)&bars[0].foo) / sizeof *bars;
}

Ow, that's convoluted. AFAICT, it's not strictly portable, but
you'd be hard put to find an implementation where it wouldn't
work. It would require willful perversity of the implementor.


Are you refering to the difference exceding the range of ptrdiff_t?


No, I'm referring to the comparison of two pointers which do not
point to the same (struct Foo) object. AFAICT, it would be legal
if the pointers were struct Bar pointers, since then they'd be
pointers to members of an array. Now they're pointers to members
of disjunct objects. I would be very surprised at an
implementation where this were an actual problem, of course.


Can't you use offsetof(struct bar, foo) to convert the fooptr to a
barptr, and go on from there? You can certainly do the reverse, to
convert a barptr to a fooptr.

--
"If you want to post a followup via groups.google.com, don't use
the broken "Reply" link at the bottom of the article. Click on
"show options" at the top of the article, then click on the
"Reply" at the bottom of the article headers." - Keith Thompson
Nov 14 '05 #11

P: n/a
CBFalconer <cb********@yahoo.com> wrote:
Richard Bos wrote:
No, I'm referring to the comparison of two pointers which do not
point to the same (struct Foo) object. AFAICT, it would be legal
if the pointers were struct Bar pointers, since then they'd be
pointers to members of an array. Now they're pointers to members
of disjunct objects. I would be very surprised at an
implementation where this were an actual problem, of course.


Can't you use offsetof(struct bar, foo) to convert the fooptr to a
barptr, and go on from there?


Yes, AFAICT. Which is one of the reasons why a normal implementation
would have to go out of its way to make the original fail.

Richard
Nov 14 '05 #12

This discussion thread is closed

Replies have been disabled for this discussion.