Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it legal to index into a struct?

Tags:

c++

c

struct

Regardless of how 'bad' the code is, and assuming that alignment etc are not an issue on the compiler/platform, is this undefined or broken behavior?

If I have a struct like this :-

struct data {     int a, b, c; };  struct data thing; 

Is it legal to access a, b and c as (&thing.a)[0], (&thing.a)[1], and (&thing.a)[2]?

In every case, on every compiler and platform I tried it on, with every setting I tried it 'worked'. I'm just worried that the compiler might not realize that b and thing[1] are the same thing and stores to 'b' might be put in a register and thing[1] reads the wrong value from memory (for example). In every case I tried it did the right thing though. (I realize of course that doesn't prove much)

This is not my code; it's code I have to work with, I'm interested in whether this is bad code or broken code as the different affects my priorities for changing it a great deal :)

Tagged C and C++ . I'm mostly interested in C++ but also C if it is different, just for interest.

like image 644
jcoder Avatar asked Nov 14 '16 13:11

jcoder


People also ask

Can struct be indexed?

No, there is no way. The idea of structs is to access the fields by their name. If addressing by the index is wanted, cell arrays are the best type for representing the data. C = struct2cell(S);

Can u declare an array inside a structure?

The most common use of structure in C programming is an array of structures. To declare an array of structure, first the structure must be defined and then an array variable of that type should be defined.

Can a struct have itself as a member?

A structure T cannot contain itself.


1 Answers

It is illegal 1. That's an Undefined behavior in C++.

You are taking the members in an array fashion, but here is what the C++ standard says (emphasis mine):

[dcl.array/1]: ...An object of array type contains a contiguously allocated non-empty set of N subobjects of type T...

But, for members, there's no such contiguous requirement:

[class.mem/17]: ...;Implementation alignment requirements might cause two adjacent members not to be allocated immediately after each other...

While the above two quotes should be enough to hint why indexing into a struct as you did isn't a defined behavior by the C++ standard, let's pick one example: look at the expression (&thing.a)[2] - Regarding the subscript operator:

[expr.post//expr.sub/1]: A postfix expression followed by an expression in square brackets is a postfix expression. One of the expressions shall be a glvalue of type “array of T” or a prvalue of type “pointer to T” and the other shall be a prvalue of unscoped enumeration or integral type. The result is of type “T”. The type “T” shall be a completely-defined object type.66 The expression E1[E2] is identical (by definition) to ((E1)+(E2))

Digging into the bold text of the above quote: regarding adding an integral type to a pointer type (note the emphasis here)..

[expr.add/4]: When an expression that has integral type is added to or subtracted from a pointer, the result has the type of the pointer operand. If the expression P points to element x[i] of an array object x with n elements, the expressions P + J and J + P (where J has the value j) point to the (possibly-hypothetical) element x[i + j] if 0 ≤ i + j ≤ n; otherwise, the behavior is undefined. ...

Note the array requirement for the if clause; else the otherwise in the above quote. The expression (&thing.a)[2] obviously doesn't qualify for the if clause; Hence, Undefined Behavior.


On a side note: Though I have extensively experimented the code and its variations on various compilers and they don't introduce any padding here, (it works); from a maintenance view, the code is extremely fragile. you should still assert that the implementation allocated the members contiguously before doing this. And stay in-bounds :-). But its still Undefined behavior....

Some viable workarounds (with defined behavior) have been provided by other answers.



As rightly pointed out in the comments, [basic.lval/8], which was in my previous edit doesn't apply. Thanks @2501 and @M.M.

1: See @Barry's answer to this question for the only one legal case where you can access thing.a member of the struct via this parttern.

like image 57
WhiZTiM Avatar answered Sep 17 '22 00:09

WhiZTiM