Using offsetof() values to access members of a struct

U

Urs Thuermann

With offsetof() I can get the offset of a member in a struct. AFAICS,
it is portable and clean to use this offset to access that member. I
need to do something like this

struct foo {
struct foo *next;
int a;
int b;
int c;
};

void iterate(struct foo *list, size_t off)
{
struct foo *p;
for (p = list; p; p = p->next) {
int i = *(int *)((char *)p + off); /* [1] */
/* do something with i */
}
}

void func(struct foo *f)
{
iterate(f, offsetof(struct foo, a);
iterate(f, offsetof(struct foo, b);
iterate(f, offsetof(struct foo, c);
}

[1] Is this code portable and without undefined or implementation-defined
behavior?

Or is there a better way to achieve the same?

urs
 
B

Ben Bacarisse

Urs Thuermann said:
With offsetof() I can get the offset of a member in a struct. AFAICS,
it is portable and clean to use this offset to access that member. I
need to do something like this

struct foo {
struct foo *next;
int a;
int b;
int c;
};

void iterate(struct foo *list, size_t off)
{
struct foo *p;
for (p = list; p; p = p->next) {
int i = *(int *)((char *)p + off); /* [1] */
/* do something with i */
}
}

void func(struct foo *f)
{
iterate(f, offsetof(struct foo, a);
iterate(f, offsetof(struct foo, b);
iterate(f, offsetof(struct foo, c);
}

[1] Is this code portable and without undefined or implementation-defined
behavior?

I think it is, but I am not one of the most skilled interpreters of
such matters. Stay tuned for more informed voices to come along.
Or is there a better way to achieve the same?

"Better" is a loaded word and the only information we have is that you
want portability. I would at least consider:

int access_a(struct foo *fp) { return fp->a; }
int access_b(struct foo *fp) { return fp->b; }
int access_c(struct foo *fp) { return fp->c; }

void iterate(struct foo *list, int (*access)(struct foo *))
{
struct foo *p;
for (p = list; p; p = p->next) {
int i = access(p);
/* do something with i... */
}
}

void func(struct foo *f)
{
iterate(f, access_a);
iterate(f, access_b);
iterate(f, access_c);
}
 
E

Eric Sosman

Urs said:
With offsetof() I can get the offset of a member in a struct. AFAICS,
it is portable and clean to use this offset to access that member. I
need to do something like this

struct foo {
struct foo *next;
int a;
int b;
int c;
};

void iterate(struct foo *list, size_t off)
{
struct foo *p;
for (p = list; p; p = p->next) {
int i = *(int *)((char *)p + off); /* [1] */
/* do something with i */
}
}

void func(struct foo *f)
{
iterate(f, offsetof(struct foo, a);
iterate(f, offsetof(struct foo, b);
iterate(f, offsetof(struct foo, c);
}

[1] Is this code portable and without undefined or implementation-defined
behavior?

The code at [1] is fine, but the calls to it have too few
parentheses.
Or is there a better way to achieve the same?

In this particular case, yes:

struct foo {
struct foo *next;
int abc[3];
};

.... and work with array indices instead of element offsets.
 
C

Chris Dollin

Urs said:
With offsetof() I can get the offset of a member in a struct.
Yes.

AFAICS, it is portable
Yes.

and clean

That depends.
to use this offset to access that member. I
need to do something like this

struct foo {
struct foo *next;
int a;
int b;
int c;
};

void iterate(struct foo *list, size_t off)
{
struct foo *p;
for (p = list; p; p = p->next) {
int i = *(int *)((char *)p + off); /* [1] */
/* do something with i */
}
}

void func(struct foo *f)
{
iterate(f, offsetof(struct foo, a);
iterate(f, offsetof(struct foo, b);
iterate(f, offsetof(struct foo, c);
}

[1] Is this code portable and without undefined or implementation-defined
behavior?

Looks like it to me.
Or is there a better way to achieve the same?

Well, the natural and obvious solution is to use an array of
three elements and an index, rather than playing offset games.
That reduces the line

| int i = *(int *)((char *)p + off)

to
int i = p.arrayName[off];

which is, IMAO, twenty-seven times more comprehensible. (Obviously
you call `iterate` with values 0, 1, 2, rather than the offsetof
expressions.)
 
U

Urs Thuermann

Eric Sosman said:
The code at [1] is fine, but the calls to it have too few
parentheses.

Ah, yes. The whole example is not anywhere near to my real code, and
was only quickly hacked down for this posting. In my real code,
function names, struct names, and member names and types are
different. The real struct doesn't have three ints but a dozen
pointers to further linked lists which have to be traversed.

Currently, I have a separate iterate() function for each of the struct
members and all have exactly the same structure. I wanted to reduce
code duplication by somwehow passing an argument specifying which
member to work on.
Or is there a better way to achieve the same?

In this particular case, yes:

struct foo {
struct foo *next;
int abc[3];
};

... and work with array indices instead of element offsets.

Good idea. I'll probably convert my dozen of pointers to an array and
define symbolic names for the indices. Thanks for your suggestion.


urs
 
C

Chris Torek

Others have answered the specific question; this is more general:

With offsetof() I can get the offset of a member in a struct. AFAICS,
it is portable and clean to use this offset to access that member. ...

A structure member, inside a C compiler, really provides two items:
an offset, and a type. Given those two items -- offset and type --
you can "synthesize" a member-access.

The C syntax "structure.member" essentially means "using the
structure named on the left, which should be an lvalue[%], find
the address of the object implied by that lvalue, add the offset
implied by the member, and access an object whose type is
determined by the member." This rule also works for unions,
where the offset is always zero.

The "offset add" must be done in terms of "bytes" (C bytes, i.e.,
"unsigned char"s), of course. (&structure)+1, using ordinary
(i.e., entire-object-size) pointer arithmetic, points right
past the entire structure, so any nonzero offset would be too
big if this "offset add" were not "de-scaled" down to byte size.

-----
[%] This deliberately ignores the special case of a function return
value of structured type, which has some funny constraints. In
particular, such a return value is the only way to produce an
"array rvalue", which has no address, yet can still be subscripted
to access an array element. If you somehow access the (nonexistent)
address of a struct-type return value or any of its members --
including an array or an array element -- the effect is not defined.
It is generally unwise even to try, whether or not the compiler
catches you. :)
-----

The C syntax "expr->member" essentially means "using the pointer
value given on the left, and the offset and type implied by
the member, access the object at that offset." This is of course
identical to "(*(expr)).member" since *(expr) produces the lvalue
pointed-to by the expression. Taking its address and adding the
offset has the same effect as just starting with the expression,
treating it as an address, and adding the offset.
 
H

Harald van =?UTF-8?B?RMSzaw==?=

Chris said:
[%] This deliberately ignores the special case of a function return
value of structured type, which has some funny constraints. In
particular, such a return value is the only way to produce an
"array rvalue", which has no address, yet can still be subscripted
to access an array element. If you somehow access the (nonexistent)
address of a struct-type return value or any of its members --
including an array or an array element -- the effect is not defined.
It is generally unwise even to try, whether or not the compiler
catches you. :)

In C90, an array rvalue cannot be subscripted, and in C99, I don't believe
there is anything prohibiting

struct S { int a[1]; } s;
struct S f(void) { return s; }
int main(void) { int *p; return *(p = f().a); }

(though p may not be dereferenced after the next sequence point).

And function calls are not the only way. The conditional, assignment, and
comma operators can also do it.
 

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,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top