Inelegant Access to Struct Members

c, pointers, struct, undefined-behavior

Solution

There are a few problems, the warning you are seeing is due the unspecified order of evaluation of the argument to your function. The C99 draft standard section `6.5.2.2` Function calls paragraph 10 says:

The order of evaluation of the function designator, the actual arguments, and subexpressions within the actual arguments is unspecified, but there is a sequence point before the actual call.

you are also modifying a variable more than once within a sequence point which is undefined behavior and can not be relied on to work, section `6.5` Expressions paragraph 2 says (emphasis mine going forward):

Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression.72) Furthermore, the prior value shall be read only to determine the value to be stored.73)

Also, note that the standard allows for padding between elements of a struct but beyond that scalar is considered an array of one element and so incrementing beyond the array and then performing indirection would be undefined as well. This is covered in section `6.5.6` Additive operators paragraph 7:

For the purposes of these operators, a pointer to an object that is not an element of an array behaves the same as a pointer to the first element of an array of length one with the type of the object as its element type.

and going more than one past the array bounds of accessing one past the array bounds is undefined by section `6.5.6` Additive operators paragraph 8:

[...]If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined. If the result points one past the last element of the array object, it shall not be used as the operand of a unary * operator that is evaluated.

We can see depending on the optimization level `gcc` will output (see it live):

3 3 2

or (see it live):

3 3 3

neither of which is the desired output.

The standards compliant way to access your structs members via a pointer would be to use offsetof and here, which requires including `stddef.h`. Accessing member `a` would look like this:

*( (int*) ((char*)aBadBoyPointer+offsetof(myStruct, a)) )
    ^       ^                    ^
    3       2                    1

There are three elements here:

- Use `offsetof` to determine the offset in bytes of the member

- Cast to *char ** since we need pointer arithmetic in bytes

- Cast back to *int ** since that is the correct type

Problem

Due to the way how memory is layout for `structs` and its members, I was able to do the following: ``` typedef struct { int a; int b; int c; } myStruct; ``` In `main()`: ``` myStruct aStruct; aStruct.a = 1; aStruct.b = 2; aStruct.c = 3; int *aBadBoyPointer = &aStruct.a; printf("%i %i %i", *(aBadBoyPointer), *(++aBadBoyPointer), *(++aBadBoyPointer)); ``` Easy enough. The above line causes a warning: Unsequenced modification and access to `aBadBoyPointer` But it compiles and runs fine printing out `1, 2, 3`, respectively. My question here: For the sake of doing things correctly, could you cite a scenario where this would break miserably, a scenario that confirms the compiler that this is a bad practice / bad way of doing things? Or: perhaps this is in fact a "a good way" to do things under some rare circumstances? Addendum: Aside from this part that cause Undefined Behavior: ``` printf("%i %i %i", *(aBadBoyPointer), *(++aBadBoyPointer), *(++aBadBoyPointer)); ``` What I really like to know is: Is it considered OK (a good practice) using a pointer pointing to one of the members of the `struct` but then gain access to other members within the `struct` this way (by incrementing, made possible due to the memory layout of the `struct` members) or is it a practice that is frowned upon by the Standard? And secondly as mentioned above, if this is in deed a bad practice, would there ever be cases where using a pointer this way within a struct and then gain access to another member variable beneficial under certain circumstances?

Original source

Related problems