From the GCC Documentation, but applies to any optimising compiler:
The shortcuts taken by optimized code may occasionally (sic) be surprising:
some variables you declared may not exist at all;
flow of control may briefly move where you did not expect it;
some statements may not be executed because they compute constant results or their values are already at hand;
some statements may execute in different places because they have been moved out of loops.
Judging by the many & frequent posts here, this is not just "occasionally" surprising; it is regularly - even routinely - surprising. Especially to beginners.
So, rather than keep repeating the explanation, I thought I'd capture it here - for future reference.
The GCC Manual goes on to say:
Nevertheless it is possible to debug optimized output. This makes it reasonable to use the optimizer for programs that might have bugs.
If you are not using some other optimization option, consider using -Og (see Optimize Options) with -g. With no -O option at all, some compiler passes that collect information useful for debugging do not run at all, so that -Og may result in a better debugging experience.
This last bit is specific to GCC - for other compilers, see their Documentation.