I am aware that there are other similar questions. I have read through these and don’t think this question has been answered.
Can I move between a consecutive (contiguous in the declaration sequence) sequence of fields of the same type of a struct using pointer arithmetic without consulting _Alignof?
I can move between array elements without consulting _Alignof because trailing padding is included.
However, is it possible that consecutive fields of the same type in a struct are not aligned the same way as in an array?
In other words, is it possible for this code to be undefined behavior?
struct MyStruct {
long int field_1;
int field_2;
int field_3;
};
int main(void) {
struct MyStruct my_struct;
int *field_2_ptr = &my_struct.field_2;
int field_3_value = *(field_2_ptr + 1);
}
I know that this is bad practice. I am aware of ->. I know padding can interfere in general. I want to know if padding can interfere in this specific circumstance.
This question is about C. I don’t care about C++.
So far I have compiled this with GCC and tried Valgrind to see if something is off, and compiled it with clang and UBSan. It seems to be fine, on this system (x86-64 Linux).
Strictly speaking, this is undefined behavior.
Pointer arithmetic is allowed on array objects (and single objects are treated as a 1-element array for this purpose) provided the original pointer and resulting pointer point to the same array object (or one element past the end). Additionally, a pointer to one-past-the-end may not be dereferenced, otherwise it triggers undefined behavior.
This is spelled out in sections 6.5.6p7-8 of the C standard regarding Additive operators:
The section in bold is exactly what's happening here:
Since this dereferences a pointer to one past the element of (essentially) a 1-element array, this triggers undefined behavior. That
&my_struct.field_2 + 1 == &my_struct.field_3would evaluate to true doesn't matter.For the same reason, this is also undefined behavior:
While doing the above would probably work, there's no guarantee of that. Modern compilers optimize aggressively, and they can and do assume no undefined behavior exists and exploit that assumption.
I've seen cases where, given pointers
aandbwhich point to adjacent memory,(uintptr_t)(a+1) == (uintptr_t)bis true buta+1 == bis false.