find index, given pointer to member

I

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.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
 
J

Jens.Toerring

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.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
 
R

Richard Bos

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.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
 
K

Kevin Bracey

{
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.
 
P

pete

Richard said:
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.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?
 
I

ike

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.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>
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>".
 
P

pete

(e-mail address removed)-berlin.de wrote:
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.
 
I

ike

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
 
P

pete

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.
 
R

Richard Bos

pete said:
Richard said:
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.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
 
C

CBFalconer

Richard said:
pete said:
Richard said:
(e-mail address removed) 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.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.
 
R

Richard Bos

CBFalconer said:
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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,743
Messages
2,569,478
Members
44,898
Latest member
BlairH7607

Latest Threads

Top