3

I'm being passed a pointer to a struct, and the first 8 members are the same size, can I access them by index?

 typedef struct example{
    uint64_t one;
    uint64_t two;
    uint64_t three;
    uint64_t four;
    uint64_t five;
    //etc...

    uint8_t ninth;

 } example_t;


void example_method(example_t *ptr)
{
    //can I do this?
    &ptr[2] // which would be equal to the uint64_t third?
}
2
  • 1
    It is fiddly and requires cheating that isn't guaranteed to work. You'd be better off getting the structure redesigned, perhaps using an array only, or perhaps using a union of an array and the substructure (with some caveats about that, too). It is best to design the structure to match the use patterns — if you're going to need to treat it as an array, store it as an array. Commented Mar 21, 2016 at 17:06
  • Agree with @JonathanLeffler in that you'd be better off redesigning the structure. Commented Mar 21, 2016 at 17:33

3 Answers 3

4

The reason why you cannot reliably take an struct member by index is that struct may contain arbitrary padding between members, as well as after last member. According to C11, N1570 §6.7.2.1/p15 Structure and union specifiers:

There may be unnamed padding within a structure object, but not at its beginning.

What you can do about it is to redefine your struct to contain array member (or possibly flexible array member).

Sign up to request clarification or add additional context in comments.

3 Comments

Good answer---was not aware of the flexible array member. :) Even if there were no padding, how would you access a struct member using indexing?
@blazs: AFAIR, the only standard-compliant facility to access member in such way is to use offsetof macro. One reason for that is padding, the second one is that types could be of different size (and alignment requirements).
@blazs: As Grezegorz wrote: use an array member, don't even think about something hacky. The names of the elements already imply some numerical indexing.
2

The way the code looks with now you can't, but you can get away with is if you structure things differently:

typedef union example
{
   struct {
          uint64_t one;
          uint64_t two;
          uint64_t three;
          uint64_t four;
          uint64_t five;
          //etc...

          uint8_t ninth;

    };
    uint64_t array[9];
}example_t;

now you can do:

void example_method(example_t *ptr)
{        
    ptr->array[2]; // access fields by index
}

But, a word of warning: you can run into trouble because of compiler alignment defaults- for example, suppose you add one more field, which is a char, the compiler might try to align it to 4 bytes (some have this default) which will mess things up, especially if the field is somewhere i n the middle of your struct.

4 Comments

Note that you are accessing member three when you do ptr->array[2].
@JonathanLefflerIf you're lucky, and there's no padding. The only certainty you have is that ptr[0] and ptr->one are the same
@EliasVanOotegem: If you have a sequence of N elements of identical types at the start of a structure, then I know of no compiler that will add padding between the elements — assuming they're not bit-fields. Padding appears when there are different types, and in particular when a type requiring stringent alignment follows a type requiring less stringent alignment in a structure. The ninth element (int8_t rather than int64_t) probably has no padding before it; it probably will have padding after it. But yes, it is possible for a compiler to screw you. In this, they usually don't.
@JonathanLeffler: I completely agree with you. In this particular case, it's highly unlikely for a compiler to add padding bits. However, the OP did not post the entire struct, and the last element does have a different type. If, for example, the sixth and eighth members have different types, then a union won't work properly. It's best to avoid the grey areas that surround undefined behaviour whenever possible, that's why I commented. The only thing you could do is use compiler features (like GCC's __attribute__((__packed__))) or do manual padding (architecture specific macro mess)
0

No, you cannot. Saying ptr[2] is equivalent to *(ptr+2) which first adds sizeof(struct example) to the address ptr, and then dereferences.

Of course you can have an array in the struct instead of eight members:

struct example {
    uint64_t members[8];
    uint8_t ninth;
};

in which case you can access the third element of the array with ptr->members[2]. If the names of the members are not one, two, etc. you can defined an

enum Member_Names {
    FIRST_NAME = 0,
    SECOND_NAME = 1,
    ...,
    EIGTH_NAME = 7
};

and then use ptr->members[THIRD_NAME].

3 Comments

So there is no way to access the struct by index of members?
No, I don't think there is. Of course you can have an array in the struct instead of nine members: struct example { uint64_t members[8]; uint8_t ninth; } in which case you can access the second element of the array with ptr->members[2].
I meant the third element, not the second element; sorry. :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.