This is not safe. Its not a 'concidence' -- you found the offset of the array inside your creation. But its not 'always' going to be at that location: compiler settings may move it, size of integer on a different machine may move it, optimizations may move it, and so on.
It will 'work' consistently on your compiler for your code if you do not change anything. Because this object is simple, it may even work on most compilers or even all of them. But a more complex object may get messed up.
you can dynamically reverse engineer your creation and bust it into pointer offsets inside your object, but this is just being silly, because you are doing a fair bit of code and work to get what you already know.
eg you know where data is: &data[0] is a reachable int* already, and you know your object, &a is a reachable pointer, so you can locate the offset inside your object that way.
It usually ends up being a double cast. Usually you have to break your object into bytes with a char*, find the offset, and cast that offset back to type* (here, that is int). Its just pure luck that your ints are at a fixed integer offset from the start of the struct (or your alignment settings) ... it could be offset at byte 17 instead.