>May I ask what the values in this line of code are for?
static volatile uint32_t* GpioA_ODR = (volatile uint32_t*)(0x40010800+0x0C);
GpioA_ODR is a pointer to a volatile uint32_t and its address is 0x40010800C which was derived from the datasheet (the addition is done in parens so the byte offset address listed in the datasheet can be used, as without parens the addition is done to the first value treated as pointer address, and since the pointer is pointing to a 4byte value/uint32_t, the addition will be 0x0C*4 which is not what is wanted).
The address 'stored' in the var GpioA_ODR has nothing to do with what it is pointing to, so the 0x4001 has nothing to do with the upper bits of ODR. This would also be correct-
static volatile uint16_t* GpioA_ODR = (volatile uint16_t*)(0x40010800+0x0C);
where GpioA_ODR still stores the same address, but now its pointing to a uint16_t instead. Probably a better idea as you will then get a warning of you try to write anything other than a 16bit value. Was a simple example, so did not worry about it.
You may want to figure out what the code you wrote does. Decode it using the headers, and you will not be too far from what I wrote. The online compiler does not have headers for an mcu (with some exceptions), so I created my own little 'header' to create an example. All headers, drivers, and code ultimately ends up as standard C code for the compiler to digest, so if you take the time to figure out what a header/driver may be doing you will also discover what the C language can do, as it all ends up there. Once you know how the sausage is made, you can then make your own in whatever manner you prefer.