I'm compiling the following with -O0 (recent gcc/clang) and they both give me a answer I don't expect.
ptr1++; // I now point to a.y!
(*ptr1)++; // I now incremented a.y to 3
ptr2++; // I now point to y!
(*ptr2)++; // I now incremented y to 3
std::cout << "a.y=" << a.y << " ptr1=" << *ptr1 << '\n';
std::cout << "y= " << y << " ptr2=" << *ptr2 << '\n';
y= 2 ptr2=2
In the first case dealing with class members the part you are ignoring is the compiler is allowed to add any amount of padding in between members of a object and at the end of the object. Because of this increment a pointer to one member does not have to give you the next member.
The second part of the standard you are missing is it is illegal to access memory though a pointer to what it doesn't point to. Even though
y might be there in memory the pointer is not allowed to access it. It is allowed to access
x and it is allowed to compare to see if it one past
x but it cannot dereference that one past